roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         };
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             };
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa 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  * @cfg {Boolean} clickable (true|false) default false
995
996  *     
997  * @constructor
998  * Create a new Container
999  * @param {Object} config The config object
1000  */
1001
1002 Roo.bootstrap.Container = function(config){
1003     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1004     
1005     this.addEvents({
1006         // raw events
1007          /**
1008          * @event expand
1009          * After the panel has been expand
1010          * 
1011          * @param {Roo.bootstrap.Container} this
1012          */
1013         "expand" : true,
1014         /**
1015          * @event collapse
1016          * After the panel has been collapsed
1017          * 
1018          * @param {Roo.bootstrap.Container} this
1019          */
1020         "collapse" : true,
1021         /**
1022          * @event click
1023          * When a element is chick
1024          * @param {Roo.bootstrap.Container} this
1025          * @param {Roo.EventObject} e
1026          */
1027         "click" : true
1028     });
1029 };
1030
1031 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1032     
1033     jumbotron : false,
1034     well: '',
1035     panel : '',
1036     header: '',
1037     footer : '',
1038     sticky: '',
1039     tag : false,
1040     alert : false,
1041     fa: false,
1042     icon : false,
1043     expandable : false,
1044     rheader : '',
1045     expanded : true,
1046     clickable: false,
1047   
1048      
1049     getChildContainer : function() {
1050         
1051         if(!this.el){
1052             return false;
1053         }
1054         
1055         if (this.panel.length) {
1056             return this.el.select('.panel-body',true).first();
1057         }
1058         
1059         return this.el;
1060     },
1061     
1062     
1063     getAutoCreate : function(){
1064         
1065         var cfg = {
1066             tag : this.tag || 'div',
1067             html : '',
1068             cls : ''
1069         };
1070         if (this.jumbotron) {
1071             cfg.cls = 'jumbotron';
1072         }
1073         
1074         
1075         
1076         // - this is applied by the parent..
1077         //if (this.cls) {
1078         //    cfg.cls = this.cls + '';
1079         //}
1080         
1081         if (this.sticky.length) {
1082             
1083             var bd = Roo.get(document.body);
1084             if (!bd.hasClass('bootstrap-sticky')) {
1085                 bd.addClass('bootstrap-sticky');
1086                 Roo.select('html',true).setStyle('height', '100%');
1087             }
1088              
1089             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1090         }
1091         
1092         
1093         if (this.well.length) {
1094             switch (this.well) {
1095                 case 'lg':
1096                 case 'sm':
1097                     cfg.cls +=' well well-' +this.well;
1098                     break;
1099                 default:
1100                     cfg.cls +=' well';
1101                     break;
1102             }
1103         }
1104         
1105         if (this.hidden) {
1106             cfg.cls += ' hidden';
1107         }
1108         
1109         
1110         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1111             cfg.cls +=' alert alert-' + this.alert;
1112         }
1113         
1114         var body = cfg;
1115         
1116         if (this.panel.length) {
1117             cfg.cls += ' panel panel-' + this.panel;
1118             cfg.cn = [];
1119             if (this.header.length) {
1120                 
1121                 var h = [];
1122                 
1123                 if(this.expandable){
1124                     
1125                     cfg.cls = cfg.cls + ' expandable';
1126                     
1127                     h.push({
1128                         tag: 'i',
1129                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1130                     });
1131                     
1132                 }
1133                 
1134                 h.push(
1135                     {
1136                         tag: 'span',
1137                         cls : 'panel-title',
1138                         html : (this.expandable ? '&nbsp;' : '') + this.header
1139                     },
1140                     {
1141                         tag: 'span',
1142                         cls: 'panel-header-right',
1143                         html: this.rheader
1144                     }
1145                 );
1146                 
1147                 cfg.cn.push({
1148                     cls : 'panel-heading',
1149                     style : this.expandable ? 'cursor: pointer' : '',
1150                     cn : h
1151                 });
1152                 
1153             }
1154             
1155             body = false;
1156             cfg.cn.push({
1157                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1158                 html : this.html
1159             });
1160             
1161             
1162             if (this.footer.length) {
1163                 cfg.cn.push({
1164                     cls : 'panel-footer',
1165                     html : this.footer
1166                     
1167                 });
1168             }
1169             
1170         }
1171         
1172         if (body) {
1173             body.html = this.html || cfg.html;
1174             // prefix with the icons..
1175             if (this.fa) {
1176                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1177             }
1178             if (this.icon) {
1179                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1180             }
1181             
1182             
1183         }
1184         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1185             cfg.cls =  'container';
1186         }
1187         
1188         return cfg;
1189     },
1190     
1191     initEvents: function() 
1192     {
1193         if(this.expandable){
1194             var headerEl = this.headerEl();
1195         
1196             if(headerEl){
1197                 headerEl.on('click', this.onToggleClick, this);
1198             }
1199         }
1200         
1201         if(this.clickable){
1202             this.el.on('click', this.onClick, this);
1203         }
1204         
1205     },
1206     
1207     onToggleClick : function()
1208     {
1209         var headerEl = this.headerEl();
1210         
1211         if(!headerEl){
1212             return;
1213         }
1214         
1215         if(this.expanded){
1216             this.collapse();
1217             return;
1218         }
1219         
1220         this.expand();
1221     },
1222     
1223     expand : function()
1224     {
1225         if(this.fireEvent('expand', this)) {
1226             
1227             this.expanded = true;
1228             
1229             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1230             
1231             this.el.select('.panel-body',true).first().removeClass('hide');
1232             
1233             var toggleEl = this.toggleEl();
1234
1235             if(!toggleEl){
1236                 return;
1237             }
1238
1239             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1240         }
1241         
1242     },
1243     
1244     collapse : function()
1245     {
1246         if(this.fireEvent('collapse', this)) {
1247             
1248             this.expanded = false;
1249             
1250             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1251             this.el.select('.panel-body',true).first().addClass('hide');
1252         
1253             var toggleEl = this.toggleEl();
1254
1255             if(!toggleEl){
1256                 return;
1257             }
1258
1259             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1260         }
1261     },
1262     
1263     toggleEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading .fa',true).first();
1270     },
1271     
1272     headerEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-heading',true).first()
1279     },
1280     
1281     titleEl : function()
1282     {
1283         if(!this.el || !this.panel.length || !this.header.length){
1284             return;
1285         }
1286         
1287         return this.el.select('.panel-title',true).first();
1288     },
1289     
1290     setTitle : function(v)
1291     {
1292         var titleEl = this.titleEl();
1293         
1294         if(!titleEl){
1295             return;
1296         }
1297         
1298         titleEl.dom.innerHTML = v;
1299     },
1300     
1301     getTitle : function()
1302     {
1303         
1304         var titleEl = this.titleEl();
1305         
1306         if(!titleEl){
1307             return '';
1308         }
1309         
1310         return titleEl.dom.innerHTML;
1311     },
1312     
1313     setRightTitle : function(v)
1314     {
1315         var t = this.el.select('.panel-header-right',true).first();
1316         
1317         if(!t){
1318             return;
1319         }
1320         
1321         t.dom.innerHTML = v;
1322     },
1323     
1324     onClick : function(e)
1325     {
1326         e.preventDefault();
1327         
1328         this.fireEvent('click', this, e);
1329     }
1330    
1331 });
1332
1333  /*
1334  * - LGPL
1335  *
1336  * image
1337  * 
1338  */
1339
1340
1341 /**
1342  * @class Roo.bootstrap.Img
1343  * @extends Roo.bootstrap.Component
1344  * Bootstrap Img class
1345  * @cfg {Boolean} imgResponsive false | true
1346  * @cfg {String} border rounded | circle | thumbnail
1347  * @cfg {String} src image source
1348  * @cfg {String} alt image alternative text
1349  * @cfg {String} href a tag href
1350  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1351  * @cfg {String} xsUrl xs image source
1352  * @cfg {String} smUrl sm image source
1353  * @cfg {String} mdUrl md image source
1354  * @cfg {String} lgUrl lg image source
1355  * 
1356  * @constructor
1357  * Create a new Input
1358  * @param {Object} config The config object
1359  */
1360
1361 Roo.bootstrap.Img = function(config){
1362     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1363     
1364     this.addEvents({
1365         // img events
1366         /**
1367          * @event click
1368          * The img click event for the img.
1369          * @param {Roo.EventObject} e
1370          */
1371         "click" : true
1372     });
1373 };
1374
1375 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1376     
1377     imgResponsive: true,
1378     border: '',
1379     src: 'about:blank',
1380     href: false,
1381     target: false,
1382     xsUrl: '',
1383     smUrl: '',
1384     mdUrl: '',
1385     lgUrl: '',
1386
1387     getAutoCreate : function()
1388     {   
1389         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1390             return this.createSingleImg();
1391         }
1392         
1393         var cfg = {
1394             tag: 'div',
1395             cls: 'roo-image-responsive-group',
1396             cn: []
1397         };
1398         var _this = this;
1399         
1400         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1401             
1402             if(!_this[size + 'Url']){
1403                 return;
1404             }
1405             
1406             var img = {
1407                 tag: 'img',
1408                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1409                 html: _this.html || cfg.html,
1410                 src: _this[size + 'Url']
1411             };
1412             
1413             img.cls += ' roo-image-responsive-' + size;
1414             
1415             var s = ['xs', 'sm', 'md', 'lg'];
1416             
1417             s.splice(s.indexOf(size), 1);
1418             
1419             Roo.each(s, function(ss){
1420                 img.cls += ' hidden-' + ss;
1421             });
1422             
1423             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1424                 cfg.cls += ' img-' + _this.border;
1425             }
1426             
1427             if(_this.alt){
1428                 cfg.alt = _this.alt;
1429             }
1430             
1431             if(_this.href){
1432                 var a = {
1433                     tag: 'a',
1434                     href: _this.href,
1435                     cn: [
1436                         img
1437                     ]
1438                 };
1439
1440                 if(this.target){
1441                     a.target = _this.target;
1442                 }
1443             }
1444             
1445             cfg.cn.push((_this.href) ? a : img);
1446             
1447         });
1448         
1449         return cfg;
1450     },
1451     
1452     createSingleImg : function()
1453     {
1454         var cfg = {
1455             tag: 'img',
1456             cls: (this.imgResponsive) ? 'img-responsive' : '',
1457             html : null,
1458             src : 'about:blank'  // just incase src get's set to undefined?!?
1459         };
1460         
1461         cfg.html = this.html || cfg.html;
1462         
1463         cfg.src = this.src || cfg.src;
1464         
1465         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1466             cfg.cls += ' img-' + this.border;
1467         }
1468         
1469         if(this.alt){
1470             cfg.alt = this.alt;
1471         }
1472         
1473         if(this.href){
1474             var a = {
1475                 tag: 'a',
1476                 href: this.href,
1477                 cn: [
1478                     cfg
1479                 ]
1480             };
1481             
1482             if(this.target){
1483                 a.target = this.target;
1484             }
1485             
1486         }
1487         
1488         return (this.href) ? a : cfg;
1489     },
1490     
1491     initEvents: function() 
1492     {
1493         if(!this.href){
1494             this.el.on('click', this.onClick, this);
1495         }
1496         
1497     },
1498     
1499     onClick : function(e)
1500     {
1501         Roo.log('img onclick');
1502         this.fireEvent('click', this, e);
1503     }
1504    
1505 });
1506
1507  /*
1508  * - LGPL
1509  *
1510  * image
1511  * 
1512  */
1513
1514
1515 /**
1516  * @class Roo.bootstrap.Link
1517  * @extends Roo.bootstrap.Component
1518  * Bootstrap Link Class
1519  * @cfg {String} alt image alternative text
1520  * @cfg {String} href a tag href
1521  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1522  * @cfg {String} html the content of the link.
1523  * @cfg {String} anchor name for the anchor link
1524
1525  * @cfg {Boolean} preventDefault (true | false) default false
1526
1527  * 
1528  * @constructor
1529  * Create a new Input
1530  * @param {Object} config The config object
1531  */
1532
1533 Roo.bootstrap.Link = function(config){
1534     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1535     
1536     this.addEvents({
1537         // img events
1538         /**
1539          * @event click
1540          * The img click event for the img.
1541          * @param {Roo.EventObject} e
1542          */
1543         "click" : true
1544     });
1545 };
1546
1547 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1548     
1549     href: false,
1550     target: false,
1551     preventDefault: false,
1552     anchor : false,
1553     alt : false,
1554
1555     getAutoCreate : function()
1556     {
1557         
1558         var cfg = {
1559             tag: 'a'
1560         };
1561         // anchor's do not require html/href...
1562         if (this.anchor === false) {
1563             cfg.html = this.html || '';
1564             cfg.href = this.href || '#';
1565         } else {
1566             cfg.name = this.anchor;
1567             if (this.html !== false) {
1568                 cfg.html = this.html;
1569             }
1570             if (this.href !== false) {
1571                 cfg.href = this.href;
1572             }
1573         }
1574         
1575         if(this.alt !== false){
1576             cfg.alt = this.alt;
1577         }
1578         
1579         
1580         if(this.target !== false) {
1581             cfg.target = this.target;
1582         }
1583         
1584         return cfg;
1585     },
1586     
1587     initEvents: function() {
1588         
1589         if(!this.href || this.preventDefault){
1590             this.el.on('click', this.onClick, this);
1591         }
1592     },
1593     
1594     onClick : function(e)
1595     {
1596         if(this.preventDefault){
1597             e.preventDefault();
1598         }
1599         //Roo.log('img onclick');
1600         this.fireEvent('click', this, e);
1601     }
1602    
1603 });
1604
1605  /*
1606  * - LGPL
1607  *
1608  * header
1609  * 
1610  */
1611
1612 /**
1613  * @class Roo.bootstrap.Header
1614  * @extends Roo.bootstrap.Component
1615  * Bootstrap Header class
1616  * @cfg {String} html content of header
1617  * @cfg {Number} level (1|2|3|4|5|6) default 1
1618  * 
1619  * @constructor
1620  * Create a new Header
1621  * @param {Object} config The config object
1622  */
1623
1624
1625 Roo.bootstrap.Header  = function(config){
1626     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1627 };
1628
1629 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1630     
1631     //href : false,
1632     html : false,
1633     level : 1,
1634     
1635     
1636     
1637     getAutoCreate : function(){
1638         
1639         
1640         
1641         var cfg = {
1642             tag: 'h' + (1 *this.level),
1643             html: this.html || ''
1644         } ;
1645         
1646         return cfg;
1647     }
1648    
1649 });
1650
1651  
1652
1653  /*
1654  * Based on:
1655  * Ext JS Library 1.1.1
1656  * Copyright(c) 2006-2007, Ext JS, LLC.
1657  *
1658  * Originally Released Under LGPL - original licence link has changed is not relivant.
1659  *
1660  * Fork - LGPL
1661  * <script type="text/javascript">
1662  */
1663  
1664 /**
1665  * @class Roo.bootstrap.MenuMgr
1666  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1667  * @singleton
1668  */
1669 Roo.bootstrap.MenuMgr = function(){
1670    var menus, active, groups = {}, attached = false, lastShow = new Date();
1671
1672    // private - called when first menu is created
1673    function init(){
1674        menus = {};
1675        active = new Roo.util.MixedCollection();
1676        Roo.get(document).addKeyListener(27, function(){
1677            if(active.length > 0){
1678                hideAll();
1679            }
1680        });
1681    }
1682
1683    // private
1684    function hideAll(){
1685        if(active && active.length > 0){
1686            var c = active.clone();
1687            c.each(function(m){
1688                m.hide();
1689            });
1690        }
1691    }
1692
1693    // private
1694    function onHide(m){
1695        active.remove(m);
1696        if(active.length < 1){
1697            Roo.get(document).un("mouseup", onMouseDown);
1698             
1699            attached = false;
1700        }
1701    }
1702
1703    // private
1704    function onShow(m){
1705        var last = active.last();
1706        lastShow = new Date();
1707        active.add(m);
1708        if(!attached){
1709           Roo.get(document).on("mouseup", onMouseDown);
1710            
1711            attached = true;
1712        }
1713        if(m.parentMenu){
1714           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1715           m.parentMenu.activeChild = m;
1716        }else if(last && last.isVisible()){
1717           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1718        }
1719    }
1720
1721    // private
1722    function onBeforeHide(m){
1723        if(m.activeChild){
1724            m.activeChild.hide();
1725        }
1726        if(m.autoHideTimer){
1727            clearTimeout(m.autoHideTimer);
1728            delete m.autoHideTimer;
1729        }
1730    }
1731
1732    // private
1733    function onBeforeShow(m){
1734        var pm = m.parentMenu;
1735        if(!pm && !m.allowOtherMenus){
1736            hideAll();
1737        }else if(pm && pm.activeChild && active != m){
1738            pm.activeChild.hide();
1739        }
1740    }
1741
1742    // private this should really trigger on mouseup..
1743    function onMouseDown(e){
1744         Roo.log("on Mouse Up");
1745         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1746             Roo.log("hideAll");
1747             hideAll();
1748             e.stopEvent();
1749         }
1750         
1751         
1752    }
1753
1754    // private
1755    function onBeforeCheck(mi, state){
1756        if(state){
1757            var g = groups[mi.group];
1758            for(var i = 0, l = g.length; i < l; i++){
1759                if(g[i] != mi){
1760                    g[i].setChecked(false);
1761                }
1762            }
1763        }
1764    }
1765
1766    return {
1767
1768        /**
1769         * Hides all menus that are currently visible
1770         */
1771        hideAll : function(){
1772             hideAll();  
1773        },
1774
1775        // private
1776        register : function(menu){
1777            if(!menus){
1778                init();
1779            }
1780            menus[menu.id] = menu;
1781            menu.on("beforehide", onBeforeHide);
1782            menu.on("hide", onHide);
1783            menu.on("beforeshow", onBeforeShow);
1784            menu.on("show", onShow);
1785            var g = menu.group;
1786            if(g && menu.events["checkchange"]){
1787                if(!groups[g]){
1788                    groups[g] = [];
1789                }
1790                groups[g].push(menu);
1791                menu.on("checkchange", onCheck);
1792            }
1793        },
1794
1795         /**
1796          * Returns a {@link Roo.menu.Menu} object
1797          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1798          * be used to generate and return a new Menu instance.
1799          */
1800        get : function(menu){
1801            if(typeof menu == "string"){ // menu id
1802                return menus[menu];
1803            }else if(menu.events){  // menu instance
1804                return menu;
1805            }
1806            /*else if(typeof menu.length == 'number'){ // array of menu items?
1807                return new Roo.bootstrap.Menu({items:menu});
1808            }else{ // otherwise, must be a config
1809                return new Roo.bootstrap.Menu(menu);
1810            }
1811            */
1812            return false;
1813        },
1814
1815        // private
1816        unregister : function(menu){
1817            delete menus[menu.id];
1818            menu.un("beforehide", onBeforeHide);
1819            menu.un("hide", onHide);
1820            menu.un("beforeshow", onBeforeShow);
1821            menu.un("show", onShow);
1822            var g = menu.group;
1823            if(g && menu.events["checkchange"]){
1824                groups[g].remove(menu);
1825                menu.un("checkchange", onCheck);
1826            }
1827        },
1828
1829        // private
1830        registerCheckable : function(menuItem){
1831            var g = menuItem.group;
1832            if(g){
1833                if(!groups[g]){
1834                    groups[g] = [];
1835                }
1836                groups[g].push(menuItem);
1837                menuItem.on("beforecheckchange", onBeforeCheck);
1838            }
1839        },
1840
1841        // private
1842        unregisterCheckable : function(menuItem){
1843            var g = menuItem.group;
1844            if(g){
1845                groups[g].remove(menuItem);
1846                menuItem.un("beforecheckchange", onBeforeCheck);
1847            }
1848        }
1849    };
1850 }();/*
1851  * - LGPL
1852  *
1853  * menu
1854  * 
1855  */
1856
1857 /**
1858  * @class Roo.bootstrap.Menu
1859  * @extends Roo.bootstrap.Component
1860  * Bootstrap Menu class - container for MenuItems
1861  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1862  * 
1863  * @constructor
1864  * Create a new Menu
1865  * @param {Object} config The config object
1866  */
1867
1868
1869 Roo.bootstrap.Menu = function(config){
1870     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1871     if (this.registerMenu) {
1872         Roo.bootstrap.MenuMgr.register(this);
1873     }
1874     this.addEvents({
1875         /**
1876          * @event beforeshow
1877          * Fires before this menu is displayed
1878          * @param {Roo.menu.Menu} this
1879          */
1880         beforeshow : true,
1881         /**
1882          * @event beforehide
1883          * Fires before this menu is hidden
1884          * @param {Roo.menu.Menu} this
1885          */
1886         beforehide : true,
1887         /**
1888          * @event show
1889          * Fires after this menu is displayed
1890          * @param {Roo.menu.Menu} this
1891          */
1892         show : true,
1893         /**
1894          * @event hide
1895          * Fires after this menu is hidden
1896          * @param {Roo.menu.Menu} this
1897          */
1898         hide : true,
1899         /**
1900          * @event click
1901          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1902          * @param {Roo.menu.Menu} this
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          * @param {Roo.EventObject} e
1905          */
1906         click : true,
1907         /**
1908          * @event mouseover
1909          * Fires when the mouse is hovering over this menu
1910          * @param {Roo.menu.Menu} this
1911          * @param {Roo.EventObject} e
1912          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1913          */
1914         mouseover : true,
1915         /**
1916          * @event mouseout
1917          * Fires when the mouse exits this menu
1918          * @param {Roo.menu.Menu} this
1919          * @param {Roo.EventObject} e
1920          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1921          */
1922         mouseout : true,
1923         /**
1924          * @event itemclick
1925          * Fires when a menu item contained in this menu is clicked
1926          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1927          * @param {Roo.EventObject} e
1928          */
1929         itemclick: true
1930     });
1931     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1935     
1936    /// html : false,
1937     //align : '',
1938     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1939     type: false,
1940     /**
1941      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1942      */
1943     registerMenu : true,
1944     
1945     menuItems :false, // stores the menu items..
1946     
1947     hidden:true,
1948     
1949     parentMenu : false,
1950     
1951     getChildContainer : function() {
1952         return this.el;  
1953     },
1954     
1955     getAutoCreate : function(){
1956          
1957         //if (['right'].indexOf(this.align)!==-1) {
1958         //    cfg.cn[1].cls += ' pull-right'
1959         //}
1960         
1961         
1962         var cfg = {
1963             tag : 'ul',
1964             cls : 'dropdown-menu' ,
1965             style : 'z-index:1000'
1966             
1967         };
1968         
1969         if (this.type === 'submenu') {
1970             cfg.cls = 'submenu active';
1971         }
1972         if (this.type === 'treeview') {
1973             cfg.cls = 'treeview-menu';
1974         }
1975         
1976         return cfg;
1977     },
1978     initEvents : function() {
1979         
1980        // Roo.log("ADD event");
1981        // Roo.log(this.triggerEl.dom);
1982         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1983         
1984         this.triggerEl.addClass('dropdown-toggle');
1985         
1986         if (Roo.isTouch) {
1987             this.el.on('touchstart'  , this.onTouch, this);
1988         }
1989         this.el.on('click' , this.onClick, this);
1990
1991         this.el.on("mouseover", this.onMouseOver, this);
1992         this.el.on("mouseout", this.onMouseOut, this);
1993         
1994     },
1995     
1996     findTargetItem : function(e)
1997     {
1998         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1999         if(!t){
2000             return false;
2001         }
2002         //Roo.log(t);         Roo.log(t.id);
2003         if(t && t.id){
2004             //Roo.log(this.menuitems);
2005             return this.menuitems.get(t.id);
2006             
2007             //return this.items.get(t.menuItemId);
2008         }
2009         
2010         return false;
2011     },
2012     
2013     onTouch : function(e) 
2014     {
2015         //e.stopEvent(); this make the user popdown broken
2016         this.onClick(e);
2017     },
2018     
2019     onClick : function(e)
2020     {
2021         Roo.log("menu.onClick");
2022         var t = this.findTargetItem(e);
2023         if(!t || t.isContainer){
2024             return;
2025         }
2026         Roo.log(e);
2027         /*
2028         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2029             if(t == this.activeItem && t.shouldDeactivate(e)){
2030                 this.activeItem.deactivate();
2031                 delete this.activeItem;
2032                 return;
2033             }
2034             if(t.canActivate){
2035                 this.setActiveItem(t, true);
2036             }
2037             return;
2038             
2039             
2040         }
2041         */
2042        
2043         Roo.log('pass click event');
2044         
2045         t.onClick(e);
2046         
2047         this.fireEvent("click", this, t, e);
2048         
2049         this.hide();
2050     },
2051      onMouseOver : function(e){
2052         var t  = this.findTargetItem(e);
2053         //Roo.log(t);
2054         //if(t){
2055         //    if(t.canActivate && !t.disabled){
2056         //        this.setActiveItem(t, true);
2057         //    }
2058         //}
2059         
2060         this.fireEvent("mouseover", this, e, t);
2061     },
2062     isVisible : function(){
2063         return !this.hidden;
2064     },
2065      onMouseOut : function(e){
2066         var t  = this.findTargetItem(e);
2067         
2068         //if(t ){
2069         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2070         //        this.activeItem.deactivate();
2071         //        delete this.activeItem;
2072         //    }
2073         //}
2074         this.fireEvent("mouseout", this, e, t);
2075     },
2076     
2077     
2078     /**
2079      * Displays this menu relative to another element
2080      * @param {String/HTMLElement/Roo.Element} element The element to align to
2081      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2082      * the element (defaults to this.defaultAlign)
2083      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2084      */
2085     show : function(el, pos, parentMenu){
2086         this.parentMenu = parentMenu;
2087         if(!this.el){
2088             this.render();
2089         }
2090         this.fireEvent("beforeshow", this);
2091         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2092     },
2093      /**
2094      * Displays this menu at a specific xy position
2095      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2096      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2097      */
2098     showAt : function(xy, parentMenu, /* private: */_e){
2099         this.parentMenu = parentMenu;
2100         if(!this.el){
2101             this.render();
2102         }
2103         if(_e !== false){
2104             this.fireEvent("beforeshow", this);
2105             //xy = this.el.adjustForConstraints(xy);
2106         }
2107         
2108         //this.el.show();
2109         this.hideMenuItems();
2110         this.hidden = false;
2111         this.triggerEl.addClass('open');
2112         
2113         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2114             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2115         }
2116         
2117         this.el.setXY(xy);
2118         this.focus();
2119         this.fireEvent("show", this);
2120     },
2121     
2122     focus : function(){
2123         return;
2124         if(!this.hidden){
2125             this.doFocus.defer(50, this);
2126         }
2127     },
2128
2129     doFocus : function(){
2130         if(!this.hidden){
2131             this.focusEl.focus();
2132         }
2133     },
2134
2135     /**
2136      * Hides this menu and optionally all parent menus
2137      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2138      */
2139     hide : function(deep){
2140         
2141         this.hideMenuItems();
2142         if(this.el && this.isVisible()){
2143             this.fireEvent("beforehide", this);
2144             if(this.activeItem){
2145                 this.activeItem.deactivate();
2146                 this.activeItem = null;
2147             }
2148             this.triggerEl.removeClass('open');;
2149             this.hidden = true;
2150             this.fireEvent("hide", this);
2151         }
2152         if(deep === true && this.parentMenu){
2153             this.parentMenu.hide(true);
2154         }
2155     },
2156     
2157     onTriggerPress  : function(e)
2158     {
2159         
2160         Roo.log('trigger press');
2161         //Roo.log(e.getTarget());
2162        // Roo.log(this.triggerEl.dom);
2163         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2164             return;
2165         }
2166         
2167         if (this.isVisible()) {
2168             Roo.log('hide');
2169             this.hide();
2170         } else {
2171             Roo.log('show');
2172             this.show(this.triggerEl, false, false);
2173         }
2174         
2175         e.stopEvent();
2176     },
2177     
2178          
2179        
2180     
2181     hideMenuItems : function()
2182     {
2183         //$(backdrop).remove()
2184         Roo.select('.open',true).each(function(aa) {
2185             
2186             aa.removeClass('open');
2187           //var parent = getParent($(this))
2188           //var relatedTarget = { relatedTarget: this }
2189           
2190            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2191           //if (e.isDefaultPrevented()) return
2192            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2193         })
2194     },
2195     addxtypeChild : function (tree, cntr) {
2196         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2197           
2198         this.menuitems.add(comp);
2199         return comp;
2200
2201     },
2202     getEl : function()
2203     {
2204         Roo.log(this.el);
2205         return this.el;
2206     }
2207 });
2208
2209  
2210  /*
2211  * - LGPL
2212  *
2213  * menu item
2214  * 
2215  */
2216
2217
2218 /**
2219  * @class Roo.bootstrap.MenuItem
2220  * @extends Roo.bootstrap.Component
2221  * Bootstrap MenuItem class
2222  * @cfg {String} html the menu label
2223  * @cfg {String} href the link
2224  * @cfg {Boolean} preventDefault (true | false) default true
2225  * @cfg {Boolean} isContainer (true | false) default false
2226  * 
2227  * 
2228  * @constructor
2229  * Create a new MenuItem
2230  * @param {Object} config The config object
2231  */
2232
2233
2234 Roo.bootstrap.MenuItem = function(config){
2235     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2236     this.addEvents({
2237         // raw events
2238         /**
2239          * @event click
2240          * The raw click event for the entire grid.
2241          * @param {Roo.bootstrap.MenuItem} this
2242          * @param {Roo.EventObject} e
2243          */
2244         "click" : true
2245     });
2246 };
2247
2248 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2249     
2250     href : false,
2251     html : false,
2252     preventDefault: true,
2253     isContainer : false,
2254     
2255     getAutoCreate : function(){
2256         
2257         if(this.isContainer){
2258             return {
2259                 tag: 'li',
2260                 cls: 'dropdown-menu-item'
2261             };
2262         }
2263         
2264         var cfg= {
2265             tag: 'li',
2266             cls: 'dropdown-menu-item',
2267             cn: [
2268                     {
2269                         tag : 'a',
2270                         href : '#',
2271                         html : 'Link'
2272                     }
2273                 ]
2274         };
2275         if (this.parent().type == 'treeview') {
2276             cfg.cls = 'treeview-menu';
2277         }
2278         
2279         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2280         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2281         return cfg;
2282     },
2283     
2284     initEvents: function() {
2285         
2286         //this.el.select('a').on('click', this.onClick, this);
2287         
2288     },
2289     onClick : function(e)
2290     {
2291         Roo.log('item on click ');
2292         //if(this.preventDefault){
2293         //    e.preventDefault();
2294         //}
2295         //this.parent().hideMenuItems();
2296         
2297         this.fireEvent('click', this, e);
2298     },
2299     getEl : function()
2300     {
2301         return this.el;
2302     }
2303 });
2304
2305  
2306
2307  /*
2308  * - LGPL
2309  *
2310  * menu separator
2311  * 
2312  */
2313
2314
2315 /**
2316  * @class Roo.bootstrap.MenuSeparator
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap MenuSeparator class
2319  * 
2320  * @constructor
2321  * Create a new MenuItem
2322  * @param {Object} config The config object
2323  */
2324
2325
2326 Roo.bootstrap.MenuSeparator = function(config){
2327     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2328 };
2329
2330 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2331     
2332     getAutoCreate : function(){
2333         var cfg = {
2334             cls: 'divider',
2335             tag : 'li'
2336         };
2337         
2338         return cfg;
2339     }
2340    
2341 });
2342
2343  
2344
2345  
2346 /*
2347 * Licence: LGPL
2348 */
2349
2350 /**
2351  * @class Roo.bootstrap.Modal
2352  * @extends Roo.bootstrap.Component
2353  * Bootstrap Modal class
2354  * @cfg {String} title Title of dialog
2355  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2356  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2357  * @cfg {Boolean} specificTitle default false
2358  * @cfg {Array} buttons Array of buttons or standard button set..
2359  * @cfg {String} buttonPosition (left|right|center) default right
2360  * @cfg {Boolean} animate default true
2361  * @cfg {Boolean} allow_close default true
2362  * 
2363  * @constructor
2364  * Create a new Modal Dialog
2365  * @param {Object} config The config object
2366  */
2367
2368 Roo.bootstrap.Modal = function(config){
2369     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2370     this.addEvents({
2371         // raw events
2372         /**
2373          * @event btnclick
2374          * The raw btnclick event for the button
2375          * @param {Roo.EventObject} e
2376          */
2377         "btnclick" : true
2378     });
2379     this.buttons = this.buttons || [];
2380      
2381     if (this.tmpl) {
2382         this.tmpl = Roo.factory(this.tmpl);
2383     }
2384     
2385 };
2386
2387 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2388     
2389     title : 'test dialog',
2390    
2391     buttons : false,
2392     
2393     // set on load...
2394      
2395     html: false,
2396     
2397     tmp: false,
2398     
2399     specificTitle: false,
2400     
2401     buttonPosition: 'right',
2402     
2403     allow_close : true,
2404     
2405     animate : true,
2406     
2407     
2408      // private
2409     bodyEl:  false,
2410     footerEl:  false,
2411     titleEl:  false,
2412     closeEl:  false,
2413     
2414     
2415     onRender : function(ct, position)
2416     {
2417         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2418      
2419         if(!this.el){
2420             var cfg = Roo.apply({},  this.getAutoCreate());
2421             cfg.id = Roo.id();
2422             //if(!cfg.name){
2423             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2424             //}
2425             //if (!cfg.name.length) {
2426             //    delete cfg.name;
2427            // }
2428             if (this.cls) {
2429                 cfg.cls += ' ' + this.cls;
2430             }
2431             if (this.style) {
2432                 cfg.style = this.style;
2433             }
2434             this.el = Roo.get(document.body).createChild(cfg, position);
2435         }
2436         //var type = this.el.dom.type;
2437         
2438         
2439         
2440         
2441         if(this.tabIndex !== undefined){
2442             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2443         }
2444         
2445         
2446         this.bodyEl = this.el.select('.modal-body',true).first();
2447         this.closeEl = this.el.select('.modal-header .close', true).first();
2448         this.footerEl = this.el.select('.modal-footer',true).first();
2449         this.titleEl = this.el.select('.modal-title',true).first();
2450         
2451         
2452          
2453         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2454         this.maskEl.enableDisplayMode("block");
2455         this.maskEl.hide();
2456         //this.el.addClass("x-dlg-modal");
2457     
2458         if (this.buttons.length) {
2459             Roo.each(this.buttons, function(bb) {
2460                 var b = Roo.apply({}, bb);
2461                 b.xns = b.xns || Roo.bootstrap;
2462                 b.xtype = b.xtype || 'Button';
2463                 if (typeof(b.listeners) == 'undefined') {
2464                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2465                 }
2466                 
2467                 var btn = Roo.factory(b);
2468                 
2469                 btn.onRender(this.el.select('.modal-footer div').first());
2470                 
2471             },this);
2472         }
2473         // render the children.
2474         var nitems = [];
2475         
2476         if(typeof(this.items) != 'undefined'){
2477             var items = this.items;
2478             delete this.items;
2479
2480             for(var i =0;i < items.length;i++) {
2481                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2482             }
2483         }
2484         
2485         this.items = nitems;
2486         
2487         // where are these used - they used to be body/close/footer
2488         
2489        
2490         this.initEvents();
2491         //this.el.addClass([this.fieldClass, this.cls]);
2492         
2493     },
2494     
2495     getAutoCreate : function(){
2496         
2497         
2498         var bdy = {
2499                 cls : 'modal-body',
2500                 html : this.html || ''
2501         };
2502         
2503         var title = {
2504             tag: 'h4',
2505             cls : 'modal-title',
2506             html : this.title
2507         };
2508         
2509         if(this.specificTitle){
2510             title = this.title;
2511             
2512         };
2513         
2514         var header = [];
2515         if (this.allow_close) {
2516             header.push({
2517                 tag: 'button',
2518                 cls : 'close',
2519                 html : '&times'
2520             });
2521         }
2522         header.push(title);
2523         
2524         var modal = {
2525             cls: "modal",
2526             style : 'display: none',
2527             cn : [
2528                 {
2529                     cls: "modal-dialog",
2530                     cn : [
2531                         {
2532                             cls : "modal-content",
2533                             cn : [
2534                                 {
2535                                     cls : 'modal-header',
2536                                     cn : header
2537                                 },
2538                                 bdy,
2539                                 {
2540                                     cls : 'modal-footer',
2541                                     cn : [
2542                                         {
2543                                             tag: 'div',
2544                                             cls: 'btn-' + this.buttonPosition
2545                                         }
2546                                     ]
2547                                     
2548                                 }
2549                                 
2550                                 
2551                             ]
2552                             
2553                         }
2554                     ]
2555                         
2556                 }
2557             ]
2558         };
2559         
2560         if(this.animate){
2561             modal.cls += ' fade';
2562         }
2563         
2564         return modal;
2565           
2566     },
2567     getChildContainer : function() {
2568          
2569          return this.bodyEl;
2570         
2571     },
2572     getButtonContainer : function() {
2573          return this.el.select('.modal-footer div',true).first();
2574         
2575     },
2576     initEvents : function()
2577     {
2578         if (this.allow_close) {
2579             this.closeEl.on('click', this.hide, this);
2580         }
2581         
2582         var _this = this;
2583         
2584         window.addEventListener("resize", function() { _this.resize(); } );
2585
2586     },
2587     
2588     resize : function()
2589     {
2590         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2591     },
2592     
2593     show : function() {
2594         
2595         if (!this.rendered) {
2596             this.render();
2597         }
2598         
2599         this.el.setStyle('display', 'block');
2600         
2601         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2602             var _this = this;
2603             (function(){
2604                 this.el.addClass('in');
2605             }).defer(50, this);
2606         }else{
2607             this.el.addClass('in');
2608             
2609         }
2610         
2611         // not sure how we can show data in here.. 
2612         //if (this.tmpl) {
2613         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2614         //}
2615         
2616         Roo.get(document.body).addClass("x-body-masked");
2617         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2618         this.maskEl.show();
2619         this.el.setStyle('zIndex', '10001');
2620        
2621         this.fireEvent('show', this);
2622          
2623         
2624         
2625     },
2626     hide : function()
2627     {
2628         this.maskEl.hide();
2629         Roo.get(document.body).removeClass("x-body-masked");
2630         this.el.removeClass('in');
2631         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2632         
2633         if(this.animate){ // why
2634             var _this = this;
2635             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2636         }else{
2637             this.el.setStyle('display', 'none');
2638         }
2639         
2640         this.fireEvent('hide', this);
2641     },
2642     
2643     addButton : function(str, cb)
2644     {
2645          
2646         
2647         var b = Roo.apply({}, { html : str } );
2648         b.xns = b.xns || Roo.bootstrap;
2649         b.xtype = b.xtype || 'Button';
2650         if (typeof(b.listeners) == 'undefined') {
2651             b.listeners = { click : cb.createDelegate(this)  };
2652         }
2653         
2654         var btn = Roo.factory(b);
2655            
2656         btn.onRender(this.el.select('.modal-footer div').first());
2657         
2658         return btn;   
2659        
2660     },
2661     
2662     setDefaultButton : function(btn)
2663     {
2664         //this.el.select('.modal-footer').()
2665     },
2666     resizeTo: function(w,h)
2667     {
2668         // skip..
2669     },
2670     setContentSize  : function(w, h)
2671     {
2672         
2673     },
2674     onButtonClick: function(btn,e)
2675     {
2676         //Roo.log([a,b,c]);
2677         this.fireEvent('btnclick', btn.name, e);
2678     },
2679      /**
2680      * Set the title of the Dialog
2681      * @param {String} str new Title
2682      */
2683     setTitle: function(str) {
2684         this.titleEl.dom.innerHTML = str;    
2685     },
2686     /**
2687      * Set the body of the Dialog
2688      * @param {String} str new Title
2689      */
2690     setBody: function(str) {
2691         this.bodyEl.dom.innerHTML = str;    
2692     },
2693     /**
2694      * Set the body of the Dialog using the template
2695      * @param {Obj} data - apply this data to the template and replace the body contents.
2696      */
2697     applyBody: function(obj)
2698     {
2699         if (!this.tmpl) {
2700             Roo.log("Error - using apply Body without a template");
2701             //code
2702         }
2703         this.tmpl.overwrite(this.bodyEl, obj);
2704     }
2705     
2706 });
2707
2708
2709 Roo.apply(Roo.bootstrap.Modal,  {
2710     /**
2711          * Button config that displays a single OK button
2712          * @type Object
2713          */
2714         OK :  [{
2715             name : 'ok',
2716             weight : 'primary',
2717             html : 'OK'
2718         }], 
2719         /**
2720          * Button config that displays Yes and No buttons
2721          * @type Object
2722          */
2723         YESNO : [
2724             {
2725                 name  : 'no',
2726                 html : 'No'
2727             },
2728             {
2729                 name  :'yes',
2730                 weight : 'primary',
2731                 html : 'Yes'
2732             }
2733         ],
2734         
2735         /**
2736          * Button config that displays OK and Cancel buttons
2737          * @type Object
2738          */
2739         OKCANCEL : [
2740             {
2741                name : 'cancel',
2742                 html : 'Cancel'
2743             },
2744             {
2745                 name : 'ok',
2746                 weight : 'primary',
2747                 html : 'OK'
2748             }
2749         ],
2750         /**
2751          * Button config that displays Yes, No and Cancel buttons
2752          * @type Object
2753          */
2754         YESNOCANCEL : [
2755             {
2756                 name : 'yes',
2757                 weight : 'primary',
2758                 html : 'Yes'
2759             },
2760             {
2761                 name : 'no',
2762                 html : 'No'
2763             },
2764             {
2765                 name : 'cancel',
2766                 html : 'Cancel'
2767             }
2768         ]
2769 });
2770  
2771  /*
2772  * - LGPL
2773  *
2774  * messagebox - can be used as a replace
2775  * 
2776  */
2777 /**
2778  * @class Roo.MessageBox
2779  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2780  * Example usage:
2781  *<pre><code>
2782 // Basic alert:
2783 Roo.Msg.alert('Status', 'Changes saved successfully.');
2784
2785 // Prompt for user data:
2786 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2787     if (btn == 'ok'){
2788         // process text value...
2789     }
2790 });
2791
2792 // Show a dialog using config options:
2793 Roo.Msg.show({
2794    title:'Save Changes?',
2795    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2796    buttons: Roo.Msg.YESNOCANCEL,
2797    fn: processResult,
2798    animEl: 'elId'
2799 });
2800 </code></pre>
2801  * @singleton
2802  */
2803 Roo.bootstrap.MessageBox = function(){
2804     var dlg, opt, mask, waitTimer;
2805     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2806     var buttons, activeTextEl, bwidth;
2807
2808     
2809     // private
2810     var handleButton = function(button){
2811         dlg.hide();
2812         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2813     };
2814
2815     // private
2816     var handleHide = function(){
2817         if(opt && opt.cls){
2818             dlg.el.removeClass(opt.cls);
2819         }
2820         //if(waitTimer){
2821         //    Roo.TaskMgr.stop(waitTimer);
2822         //    waitTimer = null;
2823         //}
2824     };
2825
2826     // private
2827     var updateButtons = function(b){
2828         var width = 0;
2829         if(!b){
2830             buttons["ok"].hide();
2831             buttons["cancel"].hide();
2832             buttons["yes"].hide();
2833             buttons["no"].hide();
2834             //dlg.footer.dom.style.display = 'none';
2835             return width;
2836         }
2837         dlg.footerEl.dom.style.display = '';
2838         for(var k in buttons){
2839             if(typeof buttons[k] != "function"){
2840                 if(b[k]){
2841                     buttons[k].show();
2842                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2843                     width += buttons[k].el.getWidth()+15;
2844                 }else{
2845                     buttons[k].hide();
2846                 }
2847             }
2848         }
2849         return width;
2850     };
2851
2852     // private
2853     var handleEsc = function(d, k, e){
2854         if(opt && opt.closable !== false){
2855             dlg.hide();
2856         }
2857         if(e){
2858             e.stopEvent();
2859         }
2860     };
2861
2862     return {
2863         /**
2864          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2865          * @return {Roo.BasicDialog} The BasicDialog element
2866          */
2867         getDialog : function(){
2868            if(!dlg){
2869                 dlg = new Roo.bootstrap.Modal( {
2870                     //draggable: true,
2871                     //resizable:false,
2872                     //constraintoviewport:false,
2873                     //fixedcenter:true,
2874                     //collapsible : false,
2875                     //shim:true,
2876                     //modal: true,
2877                   //  width:400,
2878                   //  height:100,
2879                     //buttonAlign:"center",
2880                     closeClick : function(){
2881                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2882                             handleButton("no");
2883                         }else{
2884                             handleButton("cancel");
2885                         }
2886                     }
2887                 });
2888                 dlg.render();
2889                 dlg.on("hide", handleHide);
2890                 mask = dlg.mask;
2891                 //dlg.addKeyListener(27, handleEsc);
2892                 buttons = {};
2893                 this.buttons = buttons;
2894                 var bt = this.buttonText;
2895                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2896                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2897                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2898                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2899                 //Roo.log(buttons);
2900                 bodyEl = dlg.bodyEl.createChild({
2901
2902                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2903                         '<textarea class="roo-mb-textarea"></textarea>' +
2904                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2905                 });
2906                 msgEl = bodyEl.dom.firstChild;
2907                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2908                 textboxEl.enableDisplayMode();
2909                 textboxEl.addKeyListener([10,13], function(){
2910                     if(dlg.isVisible() && opt && opt.buttons){
2911                         if(opt.buttons.ok){
2912                             handleButton("ok");
2913                         }else if(opt.buttons.yes){
2914                             handleButton("yes");
2915                         }
2916                     }
2917                 });
2918                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2919                 textareaEl.enableDisplayMode();
2920                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2921                 progressEl.enableDisplayMode();
2922                 var pf = progressEl.dom.firstChild;
2923                 if (pf) {
2924                     pp = Roo.get(pf.firstChild);
2925                     pp.setHeight(pf.offsetHeight);
2926                 }
2927                 
2928             }
2929             return dlg;
2930         },
2931
2932         /**
2933          * Updates the message box body text
2934          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2935          * the XHTML-compliant non-breaking space character '&amp;#160;')
2936          * @return {Roo.MessageBox} This message box
2937          */
2938         updateText : function(text){
2939             if(!dlg.isVisible() && !opt.width){
2940                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2941             }
2942             msgEl.innerHTML = text || '&#160;';
2943       
2944             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2945             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2946             var w = Math.max(
2947                     Math.min(opt.width || cw , this.maxWidth), 
2948                     Math.max(opt.minWidth || this.minWidth, bwidth)
2949             );
2950             if(opt.prompt){
2951                 activeTextEl.setWidth(w);
2952             }
2953             if(dlg.isVisible()){
2954                 dlg.fixedcenter = false;
2955             }
2956             // to big, make it scroll. = But as usual stupid IE does not support
2957             // !important..
2958             
2959             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2960                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2961                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2962             } else {
2963                 bodyEl.dom.style.height = '';
2964                 bodyEl.dom.style.overflowY = '';
2965             }
2966             if (cw > w) {
2967                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2968             } else {
2969                 bodyEl.dom.style.overflowX = '';
2970             }
2971             
2972             dlg.setContentSize(w, bodyEl.getHeight());
2973             if(dlg.isVisible()){
2974                 dlg.fixedcenter = true;
2975             }
2976             return this;
2977         },
2978
2979         /**
2980          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2981          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2982          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2983          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2984          * @return {Roo.MessageBox} This message box
2985          */
2986         updateProgress : function(value, text){
2987             if(text){
2988                 this.updateText(text);
2989             }
2990             if (pp) { // weird bug on my firefox - for some reason this is not defined
2991                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2992             }
2993             return this;
2994         },        
2995
2996         /**
2997          * Returns true if the message box is currently displayed
2998          * @return {Boolean} True if the message box is visible, else false
2999          */
3000         isVisible : function(){
3001             return dlg && dlg.isVisible();  
3002         },
3003
3004         /**
3005          * Hides the message box if it is displayed
3006          */
3007         hide : function(){
3008             if(this.isVisible()){
3009                 dlg.hide();
3010             }  
3011         },
3012
3013         /**
3014          * Displays a new message box, or reinitializes an existing message box, based on the config options
3015          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3016          * The following config object properties are supported:
3017          * <pre>
3018 Property    Type             Description
3019 ----------  ---------------  ------------------------------------------------------------------------------------
3020 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3021                                    closes (defaults to undefined)
3022 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3023                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3024 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3025                                    progress and wait dialogs will ignore this property and always hide the
3026                                    close button as they can only be closed programmatically.
3027 cls               String           A custom CSS class to apply to the message box element
3028 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3029                                    displayed (defaults to 75)
3030 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3031                                    function will be btn (the name of the button that was clicked, if applicable,
3032                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3033                                    Progress and wait dialogs will ignore this option since they do not respond to
3034                                    user actions and can only be closed programmatically, so any required function
3035                                    should be called by the same code after it closes the dialog.
3036 icon              String           A CSS class that provides a background image to be used as an icon for
3037                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3038 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3039 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3040 modal             Boolean          False to allow user interaction with the page while the message box is
3041                                    displayed (defaults to true)
3042 msg               String           A string that will replace the existing message box body text (defaults
3043                                    to the XHTML-compliant non-breaking space character '&#160;')
3044 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3045 progress          Boolean          True to display a progress bar (defaults to false)
3046 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3047 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3048 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3049 title             String           The title text
3050 value             String           The string value to set into the active textbox element if displayed
3051 wait              Boolean          True to display a progress bar (defaults to false)
3052 width             Number           The width of the dialog in pixels
3053 </pre>
3054          *
3055          * Example usage:
3056          * <pre><code>
3057 Roo.Msg.show({
3058    title: 'Address',
3059    msg: 'Please enter your address:',
3060    width: 300,
3061    buttons: Roo.MessageBox.OKCANCEL,
3062    multiline: true,
3063    fn: saveAddress,
3064    animEl: 'addAddressBtn'
3065 });
3066 </code></pre>
3067          * @param {Object} config Configuration options
3068          * @return {Roo.MessageBox} This message box
3069          */
3070         show : function(options)
3071         {
3072             
3073             // this causes nightmares if you show one dialog after another
3074             // especially on callbacks..
3075              
3076             if(this.isVisible()){
3077                 
3078                 this.hide();
3079                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3080                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3081                 Roo.log("New Dialog Message:" +  options.msg )
3082                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3083                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3084                 
3085             }
3086             var d = this.getDialog();
3087             opt = options;
3088             d.setTitle(opt.title || "&#160;");
3089             d.closeEl.setDisplayed(opt.closable !== false);
3090             activeTextEl = textboxEl;
3091             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3092             if(opt.prompt){
3093                 if(opt.multiline){
3094                     textboxEl.hide();
3095                     textareaEl.show();
3096                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3097                         opt.multiline : this.defaultTextHeight);
3098                     activeTextEl = textareaEl;
3099                 }else{
3100                     textboxEl.show();
3101                     textareaEl.hide();
3102                 }
3103             }else{
3104                 textboxEl.hide();
3105                 textareaEl.hide();
3106             }
3107             progressEl.setDisplayed(opt.progress === true);
3108             this.updateProgress(0);
3109             activeTextEl.dom.value = opt.value || "";
3110             if(opt.prompt){
3111                 dlg.setDefaultButton(activeTextEl);
3112             }else{
3113                 var bs = opt.buttons;
3114                 var db = null;
3115                 if(bs && bs.ok){
3116                     db = buttons["ok"];
3117                 }else if(bs && bs.yes){
3118                     db = buttons["yes"];
3119                 }
3120                 dlg.setDefaultButton(db);
3121             }
3122             bwidth = updateButtons(opt.buttons);
3123             this.updateText(opt.msg);
3124             if(opt.cls){
3125                 d.el.addClass(opt.cls);
3126             }
3127             d.proxyDrag = opt.proxyDrag === true;
3128             d.modal = opt.modal !== false;
3129             d.mask = opt.modal !== false ? mask : false;
3130             if(!d.isVisible()){
3131                 // force it to the end of the z-index stack so it gets a cursor in FF
3132                 document.body.appendChild(dlg.el.dom);
3133                 d.animateTarget = null;
3134                 d.show(options.animEl);
3135             }
3136             return this;
3137         },
3138
3139         /**
3140          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3141          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3142          * and closing the message box when the process is complete.
3143          * @param {String} title The title bar text
3144          * @param {String} msg The message box body text
3145          * @return {Roo.MessageBox} This message box
3146          */
3147         progress : function(title, msg){
3148             this.show({
3149                 title : title,
3150                 msg : msg,
3151                 buttons: false,
3152                 progress:true,
3153                 closable:false,
3154                 minWidth: this.minProgressWidth,
3155                 modal : true
3156             });
3157             return this;
3158         },
3159
3160         /**
3161          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3162          * If a callback function is passed it will be called after the user clicks the button, and the
3163          * id of the button that was clicked will be passed as the only parameter to the callback
3164          * (could also be the top-right close button).
3165          * @param {String} title The title bar text
3166          * @param {String} msg The message box body text
3167          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3168          * @param {Object} scope (optional) The scope of the callback function
3169          * @return {Roo.MessageBox} This message box
3170          */
3171         alert : function(title, msg, fn, scope){
3172             this.show({
3173                 title : title,
3174                 msg : msg,
3175                 buttons: this.OK,
3176                 fn: fn,
3177                 scope : scope,
3178                 modal : true
3179             });
3180             return this;
3181         },
3182
3183         /**
3184          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3185          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3186          * You are responsible for closing the message box when the process is complete.
3187          * @param {String} msg The message box body text
3188          * @param {String} title (optional) The title bar text
3189          * @return {Roo.MessageBox} This message box
3190          */
3191         wait : function(msg, title){
3192             this.show({
3193                 title : title,
3194                 msg : msg,
3195                 buttons: false,
3196                 closable:false,
3197                 progress:true,
3198                 modal:true,
3199                 width:300,
3200                 wait:true
3201             });
3202             waitTimer = Roo.TaskMgr.start({
3203                 run: function(i){
3204                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3205                 },
3206                 interval: 1000
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3213          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3214          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3215          * @param {String} title The title bar text
3216          * @param {String} msg The message box body text
3217          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3218          * @param {Object} scope (optional) The scope of the callback function
3219          * @return {Roo.MessageBox} This message box
3220          */
3221         confirm : function(title, msg, fn, scope){
3222             this.show({
3223                 title : title,
3224                 msg : msg,
3225                 buttons: this.YESNO,
3226                 fn: fn,
3227                 scope : scope,
3228                 modal : true
3229             });
3230             return this;
3231         },
3232
3233         /**
3234          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3235          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3236          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3237          * (could also be the top-right close button) and the text that was entered will be passed as the two
3238          * parameters to the callback.
3239          * @param {String} title The title bar text
3240          * @param {String} msg The message box body text
3241          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3242          * @param {Object} scope (optional) The scope of the callback function
3243          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3244          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3245          * @return {Roo.MessageBox} This message box
3246          */
3247         prompt : function(title, msg, fn, scope, multiline){
3248             this.show({
3249                 title : title,
3250                 msg : msg,
3251                 buttons: this.OKCANCEL,
3252                 fn: fn,
3253                 minWidth:250,
3254                 scope : scope,
3255                 prompt:true,
3256                 multiline: multiline,
3257                 modal : true
3258             });
3259             return this;
3260         },
3261
3262         /**
3263          * Button config that displays a single OK button
3264          * @type Object
3265          */
3266         OK : {ok:true},
3267         /**
3268          * Button config that displays Yes and No buttons
3269          * @type Object
3270          */
3271         YESNO : {yes:true, no:true},
3272         /**
3273          * Button config that displays OK and Cancel buttons
3274          * @type Object
3275          */
3276         OKCANCEL : {ok:true, cancel:true},
3277         /**
3278          * Button config that displays Yes, No and Cancel buttons
3279          * @type Object
3280          */
3281         YESNOCANCEL : {yes:true, no:true, cancel:true},
3282
3283         /**
3284          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3285          * @type Number
3286          */
3287         defaultTextHeight : 75,
3288         /**
3289          * The maximum width in pixels of the message box (defaults to 600)
3290          * @type Number
3291          */
3292         maxWidth : 600,
3293         /**
3294          * The minimum width in pixels of the message box (defaults to 100)
3295          * @type Number
3296          */
3297         minWidth : 100,
3298         /**
3299          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3300          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3301          * @type Number
3302          */
3303         minProgressWidth : 250,
3304         /**
3305          * An object containing the default button text strings that can be overriden for localized language support.
3306          * Supported properties are: ok, cancel, yes and no.
3307          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3308          * @type Object
3309          */
3310         buttonText : {
3311             ok : "OK",
3312             cancel : "Cancel",
3313             yes : "Yes",
3314             no : "No"
3315         }
3316     };
3317 }();
3318
3319 /**
3320  * Shorthand for {@link Roo.MessageBox}
3321  */
3322 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3323 Roo.Msg = Roo.Msg || Roo.MessageBox;
3324 /*
3325  * - LGPL
3326  *
3327  * navbar
3328  * 
3329  */
3330
3331 /**
3332  * @class Roo.bootstrap.Navbar
3333  * @extends Roo.bootstrap.Component
3334  * Bootstrap Navbar class
3335
3336  * @constructor
3337  * Create a new Navbar
3338  * @param {Object} config The config object
3339  */
3340
3341
3342 Roo.bootstrap.Navbar = function(config){
3343     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3344     
3345 };
3346
3347 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3348     
3349     
3350    
3351     // private
3352     navItems : false,
3353     loadMask : false,
3354     
3355     
3356     getAutoCreate : function(){
3357         
3358         
3359         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3360         
3361     },
3362     
3363     initEvents :function ()
3364     {
3365         //Roo.log(this.el.select('.navbar-toggle',true));
3366         this.el.select('.navbar-toggle',true).on('click', function() {
3367            // Roo.log('click');
3368             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3369         }, this);
3370         
3371         var mark = {
3372             tag: "div",
3373             cls:"x-dlg-mask"
3374         };
3375         
3376         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3377         
3378         var size = this.el.getSize();
3379         this.maskEl.setSize(size.width, size.height);
3380         this.maskEl.enableDisplayMode("block");
3381         this.maskEl.hide();
3382         
3383         if(this.loadMask){
3384             this.maskEl.show();
3385         }
3386     },
3387     
3388     
3389     getChildContainer : function()
3390     {
3391         if (this.el.select('.collapse').getCount()) {
3392             return this.el.select('.collapse',true).first();
3393         }
3394         
3395         return this.el;
3396     },
3397     
3398     mask : function()
3399     {
3400         this.maskEl.show();
3401     },
3402     
3403     unmask : function()
3404     {
3405         this.maskEl.hide();
3406     } 
3407     
3408     
3409     
3410     
3411 });
3412
3413
3414
3415  
3416
3417  /*
3418  * - LGPL
3419  *
3420  * navbar
3421  * 
3422  */
3423
3424 /**
3425  * @class Roo.bootstrap.NavSimplebar
3426  * @extends Roo.bootstrap.Navbar
3427  * Bootstrap Sidebar class
3428  *
3429  * @cfg {Boolean} inverse is inverted color
3430  * 
3431  * @cfg {String} type (nav | pills | tabs)
3432  * @cfg {Boolean} arrangement stacked | justified
3433  * @cfg {String} align (left | right) alignment
3434  * 
3435  * @cfg {Boolean} main (true|false) main nav bar? default false
3436  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3437  * 
3438  * @cfg {String} tag (header|footer|nav|div) default is nav 
3439
3440  * 
3441  * 
3442  * 
3443  * @constructor
3444  * Create a new Sidebar
3445  * @param {Object} config The config object
3446  */
3447
3448
3449 Roo.bootstrap.NavSimplebar = function(config){
3450     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3451 };
3452
3453 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3454     
3455     inverse: false,
3456     
3457     type: false,
3458     arrangement: '',
3459     align : false,
3460     
3461     
3462     
3463     main : false,
3464     
3465     
3466     tag : false,
3467     
3468     
3469     getAutoCreate : function(){
3470         
3471         
3472         var cfg = {
3473             tag : this.tag || 'div',
3474             cls : 'navbar'
3475         };
3476           
3477         
3478         cfg.cn = [
3479             {
3480                 cls: 'nav',
3481                 tag : 'ul'
3482             }
3483         ];
3484         
3485          
3486         this.type = this.type || 'nav';
3487         if (['tabs','pills'].indexOf(this.type)!==-1) {
3488             cfg.cn[0].cls += ' nav-' + this.type
3489         
3490         
3491         } else {
3492             if (this.type!=='nav') {
3493                 Roo.log('nav type must be nav/tabs/pills')
3494             }
3495             cfg.cn[0].cls += ' navbar-nav'
3496         }
3497         
3498         
3499         
3500         
3501         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3502             cfg.cn[0].cls += ' nav-' + this.arrangement;
3503         }
3504         
3505         
3506         if (this.align === 'right') {
3507             cfg.cn[0].cls += ' navbar-right';
3508         }
3509         
3510         if (this.inverse) {
3511             cfg.cls += ' navbar-inverse';
3512             
3513         }
3514         
3515         
3516         return cfg;
3517     
3518         
3519     }
3520     
3521     
3522     
3523 });
3524
3525
3526
3527  
3528
3529  
3530        /*
3531  * - LGPL
3532  *
3533  * navbar
3534  * 
3535  */
3536
3537 /**
3538  * @class Roo.bootstrap.NavHeaderbar
3539  * @extends Roo.bootstrap.NavSimplebar
3540  * Bootstrap Sidebar class
3541  *
3542  * @cfg {String} brand what is brand
3543  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3544  * @cfg {String} brand_href href of the brand
3545  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3546  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3547  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3548  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3549  * 
3550  * @constructor
3551  * Create a new Sidebar
3552  * @param {Object} config The config object
3553  */
3554
3555
3556 Roo.bootstrap.NavHeaderbar = function(config){
3557     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3558       
3559 };
3560
3561 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3562     
3563     position: '',
3564     brand: '',
3565     brand_href: false,
3566     srButton : true,
3567     autohide : false,
3568     desktopCenter : false,
3569    
3570     
3571     getAutoCreate : function(){
3572         
3573         var   cfg = {
3574             tag: this.nav || 'nav',
3575             cls: 'navbar',
3576             role: 'navigation',
3577             cn: []
3578         };
3579         
3580         var cn = cfg.cn;
3581         if (this.desktopCenter) {
3582             cn.push({cls : 'container', cn : []});
3583             cn = cn[0].cn;
3584         }
3585         
3586         if(this.srButton){
3587             cn.push({
3588                 tag: 'div',
3589                 cls: 'navbar-header',
3590                 cn: [
3591                     {
3592                         tag: 'button',
3593                         type: 'button',
3594                         cls: 'navbar-toggle',
3595                         'data-toggle': 'collapse',
3596                         cn: [
3597                             {
3598                                 tag: 'span',
3599                                 cls: 'sr-only',
3600                                 html: 'Toggle navigation'
3601                             },
3602                             {
3603                                 tag: 'span',
3604                                 cls: 'icon-bar'
3605                             },
3606                             {
3607                                 tag: 'span',
3608                                 cls: 'icon-bar'
3609                             },
3610                             {
3611                                 tag: 'span',
3612                                 cls: 'icon-bar'
3613                             }
3614                         ]
3615                     }
3616                 ]
3617             });
3618         }
3619         
3620         cn.push({
3621             tag: 'div',
3622             cls: 'collapse navbar-collapse',
3623             cn : []
3624         });
3625         
3626         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3627         
3628         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3629             cfg.cls += ' navbar-' + this.position;
3630             
3631             // tag can override this..
3632             
3633             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3634         }
3635         
3636         if (this.brand !== '') {
3637             cn[0].cn.push({
3638                 tag: 'a',
3639                 href: this.brand_href ? this.brand_href : '#',
3640                 cls: 'navbar-brand',
3641                 cn: [
3642                 this.brand
3643                 ]
3644             });
3645         }
3646         
3647         if(this.main){
3648             cfg.cls += ' main-nav';
3649         }
3650         
3651         
3652         return cfg;
3653
3654         
3655     },
3656     getHeaderChildContainer : function()
3657     {
3658         if (this.el.select('.navbar-header').getCount()) {
3659             return this.el.select('.navbar-header',true).first();
3660         }
3661         
3662         return this.getChildContainer();
3663     },
3664     
3665     
3666     initEvents : function()
3667     {
3668         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3669         
3670         if (this.autohide) {
3671             
3672             var prevScroll = 0;
3673             var ft = this.el;
3674             
3675             Roo.get(document).on('scroll',function(e) {
3676                 var ns = Roo.get(document).getScroll().top;
3677                 var os = prevScroll;
3678                 prevScroll = ns;
3679                 
3680                 if(ns > os){
3681                     ft.removeClass('slideDown');
3682                     ft.addClass('slideUp');
3683                     return;
3684                 }
3685                 ft.removeClass('slideUp');
3686                 ft.addClass('slideDown');
3687                  
3688               
3689           },this);
3690         }
3691     }    
3692     
3693 });
3694
3695
3696
3697  
3698
3699  /*
3700  * - LGPL
3701  *
3702  * navbar
3703  * 
3704  */
3705
3706 /**
3707  * @class Roo.bootstrap.NavSidebar
3708  * @extends Roo.bootstrap.Navbar
3709  * Bootstrap Sidebar class
3710  * 
3711  * @constructor
3712  * Create a new Sidebar
3713  * @param {Object} config The config object
3714  */
3715
3716
3717 Roo.bootstrap.NavSidebar = function(config){
3718     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3719 };
3720
3721 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3722     
3723     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3724     
3725     getAutoCreate : function(){
3726         
3727         
3728         return  {
3729             tag: 'div',
3730             cls: 'sidebar sidebar-nav'
3731         };
3732     
3733         
3734     }
3735     
3736     
3737     
3738 });
3739
3740
3741
3742  
3743
3744  /*
3745  * - LGPL
3746  *
3747  * nav group
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavGroup
3753  * @extends Roo.bootstrap.Component
3754  * Bootstrap NavGroup class
3755  * @cfg {String} align (left|right)
3756  * @cfg {Boolean} inverse
3757  * @cfg {String} type (nav|pills|tab) default nav
3758  * @cfg {String} navId - reference Id for navbar.
3759
3760  * 
3761  * @constructor
3762  * Create a new nav group
3763  * @param {Object} config The config object
3764  */
3765
3766 Roo.bootstrap.NavGroup = function(config){
3767     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3768     this.navItems = [];
3769    
3770     Roo.bootstrap.NavGroup.register(this);
3771      this.addEvents({
3772         /**
3773              * @event changed
3774              * Fires when the active item changes
3775              * @param {Roo.bootstrap.NavGroup} this
3776              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3777              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3778          */
3779         'changed': true
3780      });
3781     
3782 };
3783
3784 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3785     
3786     align: '',
3787     inverse: false,
3788     form: false,
3789     type: 'nav',
3790     navId : '',
3791     // private
3792     
3793     navItems : false, 
3794     
3795     getAutoCreate : function()
3796     {
3797         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3798         
3799         cfg = {
3800             tag : 'ul',
3801             cls: 'nav' 
3802         };
3803         
3804         if (['tabs','pills'].indexOf(this.type)!==-1) {
3805             cfg.cls += ' nav-' + this.type
3806         } else {
3807             if (this.type!=='nav') {
3808                 Roo.log('nav type must be nav/tabs/pills')
3809             }
3810             cfg.cls += ' navbar-nav'
3811         }
3812         
3813         if (this.parent().sidebar) {
3814             cfg = {
3815                 tag: 'ul',
3816                 cls: 'dashboard-menu sidebar-menu'
3817             };
3818             
3819             return cfg;
3820         }
3821         
3822         if (this.form === true) {
3823             cfg = {
3824                 tag: 'form',
3825                 cls: 'navbar-form'
3826             };
3827             
3828             if (this.align === 'right') {
3829                 cfg.cls += ' navbar-right';
3830             } else {
3831                 cfg.cls += ' navbar-left';
3832             }
3833         }
3834         
3835         if (this.align === 'right') {
3836             cfg.cls += ' navbar-right';
3837         }
3838         
3839         if (this.inverse) {
3840             cfg.cls += ' navbar-inverse';
3841             
3842         }
3843         
3844         
3845         return cfg;
3846     },
3847     /**
3848     * sets the active Navigation item
3849     * @param {Roo.bootstrap.NavItem} the new current navitem
3850     */
3851     setActiveItem : function(item)
3852     {
3853         var prev = false;
3854         Roo.each(this.navItems, function(v){
3855             if (v == item) {
3856                 return ;
3857             }
3858             if (v.isActive()) {
3859                 v.setActive(false, true);
3860                 prev = v;
3861                 
3862             }
3863             
3864         });
3865
3866         item.setActive(true, true);
3867         this.fireEvent('changed', this, item, prev);
3868         
3869         
3870     },
3871     /**
3872     * gets the active Navigation item
3873     * @return {Roo.bootstrap.NavItem} the current navitem
3874     */
3875     getActive : function()
3876     {
3877         
3878         var prev = false;
3879         Roo.each(this.navItems, function(v){
3880             
3881             if (v.isActive()) {
3882                 prev = v;
3883                 
3884             }
3885             
3886         });
3887         return prev;
3888     },
3889     
3890     indexOfNav : function()
3891     {
3892         
3893         var prev = false;
3894         Roo.each(this.navItems, function(v,i){
3895             
3896             if (v.isActive()) {
3897                 prev = i;
3898                 
3899             }
3900             
3901         });
3902         return prev;
3903     },
3904     /**
3905     * adds a Navigation item
3906     * @param {Roo.bootstrap.NavItem} the navitem to add
3907     */
3908     addItem : function(cfg)
3909     {
3910         var cn = new Roo.bootstrap.NavItem(cfg);
3911         this.register(cn);
3912         cn.parentId = this.id;
3913         cn.onRender(this.el, null);
3914         return cn;
3915     },
3916     /**
3917     * register a Navigation item
3918     * @param {Roo.bootstrap.NavItem} the navitem to add
3919     */
3920     register : function(item)
3921     {
3922         this.navItems.push( item);
3923         item.navId = this.navId;
3924     
3925     },
3926     
3927     /**
3928     * clear all the Navigation item
3929     */
3930    
3931     clearAll : function()
3932     {
3933         this.navItems = [];
3934         this.el.dom.innerHTML = '';
3935     },
3936     
3937     getNavItem: function(tabId)
3938     {
3939         var ret = false;
3940         Roo.each(this.navItems, function(e) {
3941             if (e.tabId == tabId) {
3942                ret =  e;
3943                return false;
3944             }
3945             return true;
3946             
3947         });
3948         return ret;
3949     },
3950     
3951     setActiveNext : function()
3952     {
3953         var i = this.indexOfNav(this.getActive());
3954         if (i > this.navItems.length) {
3955             return;
3956         }
3957         this.setActiveItem(this.navItems[i+1]);
3958     },
3959     setActivePrev : function()
3960     {
3961         var i = this.indexOfNav(this.getActive());
3962         if (i  < 1) {
3963             return;
3964         }
3965         this.setActiveItem(this.navItems[i-1]);
3966     },
3967     clearWasActive : function(except) {
3968         Roo.each(this.navItems, function(e) {
3969             if (e.tabId != except.tabId && e.was_active) {
3970                e.was_active = false;
3971                return false;
3972             }
3973             return true;
3974             
3975         });
3976     },
3977     getWasActive : function ()
3978     {
3979         var r = false;
3980         Roo.each(this.navItems, function(e) {
3981             if (e.was_active) {
3982                r = e;
3983                return false;
3984             }
3985             return true;
3986             
3987         });
3988         return r;
3989     }
3990     
3991     
3992 });
3993
3994  
3995 Roo.apply(Roo.bootstrap.NavGroup, {
3996     
3997     groups: {},
3998      /**
3999     * register a Navigation Group
4000     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4001     */
4002     register : function(navgrp)
4003     {
4004         this.groups[navgrp.navId] = navgrp;
4005         
4006     },
4007     /**
4008     * fetch a Navigation Group based on the navigation ID
4009     * @param {string} the navgroup to add
4010     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4011     */
4012     get: function(navId) {
4013         if (typeof(this.groups[navId]) == 'undefined') {
4014             return false;
4015             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4016         }
4017         return this.groups[navId] ;
4018     }
4019     
4020     
4021     
4022 });
4023
4024  /*
4025  * - LGPL
4026  *
4027  * row
4028  * 
4029  */
4030
4031 /**
4032  * @class Roo.bootstrap.NavItem
4033  * @extends Roo.bootstrap.Component
4034  * Bootstrap Navbar.NavItem class
4035  * @cfg {String} href  link to
4036  * @cfg {String} html content of button
4037  * @cfg {String} badge text inside badge
4038  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4039  * @cfg {String} glyphicon name of glyphicon
4040  * @cfg {String} icon name of font awesome icon
4041  * @cfg {Boolean} active Is item active
4042  * @cfg {Boolean} disabled Is item disabled
4043  
4044  * @cfg {Boolean} preventDefault (true | false) default false
4045  * @cfg {String} tabId the tab that this item activates.
4046  * @cfg {String} tagtype (a|span) render as a href or span?
4047  * @cfg {Boolean} animateRef (true|false) link to element default false  
4048   
4049  * @constructor
4050  * Create a new Navbar Item
4051  * @param {Object} config The config object
4052  */
4053 Roo.bootstrap.NavItem = function(config){
4054     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4055     this.addEvents({
4056         // raw events
4057         /**
4058          * @event click
4059          * The raw click event for the entire grid.
4060          * @param {Roo.EventObject} e
4061          */
4062         "click" : true,
4063          /**
4064             * @event changed
4065             * Fires when the active item active state changes
4066             * @param {Roo.bootstrap.NavItem} this
4067             * @param {boolean} state the new state
4068              
4069          */
4070         'changed': true,
4071         /**
4072             * @event scrollto
4073             * Fires when scroll to element
4074             * @param {Roo.bootstrap.NavItem} this
4075             * @param {Object} options
4076             * @param {Roo.EventObject} e
4077              
4078          */
4079         'scrollto': true
4080     });
4081    
4082 };
4083
4084 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4085     
4086     href: false,
4087     html: '',
4088     badge: '',
4089     icon: false,
4090     glyphicon: false,
4091     active: false,
4092     preventDefault : false,
4093     tabId : false,
4094     tagtype : 'a',
4095     disabled : false,
4096     animateRef : false,
4097     was_active : false,
4098     
4099     getAutoCreate : function(){
4100          
4101         var cfg = {
4102             tag: 'li',
4103             cls: 'nav-item'
4104             
4105         };
4106         
4107         if (this.active) {
4108             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4109         }
4110         if (this.disabled) {
4111             cfg.cls += ' disabled';
4112         }
4113         
4114         if (this.href || this.html || this.glyphicon || this.icon) {
4115             cfg.cn = [
4116                 {
4117                     tag: this.tagtype,
4118                     href : this.href || "#",
4119                     html: this.html || ''
4120                 }
4121             ];
4122             
4123             if (this.icon) {
4124                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4125             }
4126
4127             if(this.glyphicon) {
4128                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4129             }
4130             
4131             if (this.menu) {
4132                 
4133                 cfg.cn[0].html += " <span class='caret'></span>";
4134              
4135             }
4136             
4137             if (this.badge !== '') {
4138                  
4139                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4140             }
4141         }
4142         
4143         
4144         
4145         return cfg;
4146     },
4147     initEvents: function() 
4148     {
4149         if (typeof (this.menu) != 'undefined') {
4150             this.menu.parentType = this.xtype;
4151             this.menu.triggerEl = this.el;
4152             this.menu = this.addxtype(Roo.apply({}, this.menu));
4153         }
4154         
4155         this.el.select('a',true).on('click', this.onClick, this);
4156         
4157         if(this.tagtype == 'span'){
4158             this.el.select('span',true).on('click', this.onClick, this);
4159         }
4160        
4161         // at this point parent should be available..
4162         this.parent().register(this);
4163     },
4164     
4165     onClick : function(e)
4166     {
4167         if(
4168                 this.preventDefault || 
4169                 this.href == '#' 
4170         ){
4171             
4172             e.preventDefault();
4173         }
4174         
4175         if (this.disabled) {
4176             return;
4177         }
4178         
4179         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4180         if (tg && tg.transition) {
4181             Roo.log("waiting for the transitionend");
4182             return;
4183         }
4184         
4185         
4186         
4187         //Roo.log("fire event clicked");
4188         if(this.fireEvent('click', this, e) === false){
4189             return;
4190         };
4191         
4192         if(this.tagtype == 'span'){
4193             return;
4194         }
4195         
4196         //Roo.log(this.href);
4197         var ael = this.el.select('a',true).first();
4198         //Roo.log(ael);
4199         
4200         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4201             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4202             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4203                 return; // ignore... - it's a 'hash' to another page.
4204             }
4205             
4206             e.preventDefault();
4207             this.scrollToElement(e);
4208         }
4209         
4210         
4211         var p =  this.parent();
4212    
4213         if (['tabs','pills'].indexOf(p.type)!==-1) {
4214             if (typeof(p.setActiveItem) !== 'undefined') {
4215                 p.setActiveItem(this);
4216             }
4217         }
4218         
4219         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4220         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4221             // remove the collapsed menu expand...
4222             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4223         }
4224     },
4225     
4226     isActive: function () {
4227         return this.active
4228     },
4229     setActive : function(state, fire, is_was_active)
4230     {
4231         if (this.active && !state && this.navId) {
4232             this.was_active = true;
4233             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4234             if (nv) {
4235                 nv.clearWasActive(this);
4236             }
4237             
4238         }
4239         this.active = state;
4240         
4241         if (!state ) {
4242             this.el.removeClass('active');
4243         } else if (!this.el.hasClass('active')) {
4244             this.el.addClass('active');
4245         }
4246         if (fire) {
4247             this.fireEvent('changed', this, state);
4248         }
4249         
4250         // show a panel if it's registered and related..
4251         
4252         if (!this.navId || !this.tabId || !state || is_was_active) {
4253             return;
4254         }
4255         
4256         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4257         if (!tg) {
4258             return;
4259         }
4260         var pan = tg.getPanelByName(this.tabId);
4261         if (!pan) {
4262             return;
4263         }
4264         // if we can not flip to new panel - go back to old nav highlight..
4265         if (false == tg.showPanel(pan)) {
4266             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4267             if (nv) {
4268                 var onav = nv.getWasActive();
4269                 if (onav) {
4270                     onav.setActive(true, false, true);
4271                 }
4272             }
4273             
4274         }
4275         
4276         
4277         
4278     },
4279      // this should not be here...
4280     setDisabled : function(state)
4281     {
4282         this.disabled = state;
4283         if (!state ) {
4284             this.el.removeClass('disabled');
4285         } else if (!this.el.hasClass('disabled')) {
4286             this.el.addClass('disabled');
4287         }
4288         
4289     },
4290     
4291     /**
4292      * Fetch the element to display the tooltip on.
4293      * @return {Roo.Element} defaults to this.el
4294      */
4295     tooltipEl : function()
4296     {
4297         return this.el.select('' + this.tagtype + '', true).first();
4298     },
4299     
4300     scrollToElement : function(e)
4301     {
4302         var c = document.body;
4303         
4304         /*
4305          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4306          */
4307         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4308             c = document.documentElement;
4309         }
4310         
4311         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4312         
4313         if(!target){
4314             return;
4315         }
4316
4317         var o = target.calcOffsetsTo(c);
4318         
4319         var options = {
4320             target : target,
4321             value : o[1]
4322         };
4323         
4324         this.fireEvent('scrollto', this, options, e);
4325         
4326         Roo.get(c).scrollTo('top', options.value, true);
4327         
4328         return;
4329     }
4330 });
4331  
4332
4333  /*
4334  * - LGPL
4335  *
4336  * sidebar item
4337  *
4338  *  li
4339  *    <span> icon </span>
4340  *    <span> text </span>
4341  *    <span>badge </span>
4342  */
4343
4344 /**
4345  * @class Roo.bootstrap.NavSidebarItem
4346  * @extends Roo.bootstrap.NavItem
4347  * Bootstrap Navbar.NavSidebarItem class
4348  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4349  * @constructor
4350  * Create a new Navbar Button
4351  * @param {Object} config The config object
4352  */
4353 Roo.bootstrap.NavSidebarItem = function(config){
4354     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4355     this.addEvents({
4356         // raw events
4357         /**
4358          * @event click
4359          * The raw click event for the entire grid.
4360          * @param {Roo.EventObject} e
4361          */
4362         "click" : true,
4363          /**
4364             * @event changed
4365             * Fires when the active item active state changes
4366             * @param {Roo.bootstrap.NavSidebarItem} this
4367             * @param {boolean} state the new state
4368              
4369          */
4370         'changed': true
4371     });
4372    
4373 };
4374
4375 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4376     
4377     badgeWeight : 'default',
4378     
4379     getAutoCreate : function(){
4380         
4381         
4382         var a = {
4383                 tag: 'a',
4384                 href : this.href || '#',
4385                 cls: '',
4386                 html : '',
4387                 cn : []
4388         };
4389         var cfg = {
4390             tag: 'li',
4391             cls: '',
4392             cn: [ a ]
4393         };
4394         var span = {
4395             tag: 'span',
4396             html : this.html || ''
4397         };
4398         
4399         
4400         if (this.active) {
4401             cfg.cls += ' active';
4402         }
4403         
4404         if (this.disabled) {
4405             cfg.cls += ' disabled';
4406         }
4407         
4408         // left icon..
4409         if (this.glyphicon || this.icon) {
4410             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4411             a.cn.push({ tag : 'i', cls : c }) ;
4412         }
4413         // html..
4414         a.cn.push(span);
4415         // then badge..
4416         if (this.badge !== '') {
4417             
4418             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4419         }
4420         // fi
4421         if (this.menu) {
4422             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4423             a.cls += 'dropdown-toggle treeview' ;
4424             
4425         }
4426         
4427         
4428         
4429         return cfg;
4430          
4431            
4432     },
4433     
4434     initEvents : function()
4435     { 
4436         this.el.on('click', this.onClick, this);
4437        
4438     
4439         if(this.badge !== ''){
4440  
4441             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4442         }
4443         
4444     },
4445     
4446     onClick : function(e)
4447     {
4448         if(this.disabled){
4449             e.preventDefault();
4450             return;
4451         }
4452         
4453         if(this.preventDefault){
4454             e.preventDefault();
4455         }
4456         
4457         this.fireEvent('click', this);
4458     },
4459     
4460     disable : function()
4461     {
4462         this.setDisabled(true);
4463     },
4464     
4465     enable : function()
4466     {
4467         this.setDisabled(false);
4468     },
4469     
4470     setDisabled : function(state)
4471     {
4472         if(this.disabled == state){
4473             return;
4474         }
4475         
4476         this.disabled = state;
4477         
4478         if (state) {
4479             this.el.addClass('disabled');
4480             return;
4481         }
4482         
4483         this.el.removeClass('disabled');
4484         
4485         return;
4486     },
4487     
4488     setActive : function(state)
4489     {
4490         if(this.active == state){
4491             return;
4492         }
4493         
4494         this.active = state;
4495         
4496         if (state) {
4497             this.el.addClass('active');
4498             return;
4499         }
4500         
4501         this.el.removeClass('active');
4502         
4503         return;
4504     },
4505     
4506     isActive: function () 
4507     {
4508         return this.active;
4509     },
4510     
4511     setBadge : function(str)
4512     {
4513         if(!this.badgeEl){
4514             return;
4515         }
4516         
4517         this.badgeEl.dom.innerHTML = str;
4518     }
4519     
4520    
4521      
4522  
4523 });
4524  
4525
4526  /*
4527  * - LGPL
4528  *
4529  * row
4530  * 
4531  */
4532
4533 /**
4534  * @class Roo.bootstrap.Row
4535  * @extends Roo.bootstrap.Component
4536  * Bootstrap Row class (contains columns...)
4537  * 
4538  * @constructor
4539  * Create a new Row
4540  * @param {Object} config The config object
4541  */
4542
4543 Roo.bootstrap.Row = function(config){
4544     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4545 };
4546
4547 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4548     
4549     getAutoCreate : function(){
4550        return {
4551             cls: 'row clearfix'
4552        };
4553     }
4554     
4555     
4556 });
4557
4558  
4559
4560  /*
4561  * - LGPL
4562  *
4563  * element
4564  * 
4565  */
4566
4567 /**
4568  * @class Roo.bootstrap.Element
4569  * @extends Roo.bootstrap.Component
4570  * Bootstrap Element class
4571  * @cfg {String} html contents of the element
4572  * @cfg {String} tag tag of the element
4573  * @cfg {String} cls class of the element
4574  * @cfg {Boolean} preventDefault (true|false) default false
4575  * @cfg {Boolean} clickable (true|false) default false
4576  * 
4577  * @constructor
4578  * Create a new Element
4579  * @param {Object} config The config object
4580  */
4581
4582 Roo.bootstrap.Element = function(config){
4583     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4584     
4585     this.addEvents({
4586         // raw events
4587         /**
4588          * @event click
4589          * When a element is chick
4590          * @param {Roo.bootstrap.Element} this
4591          * @param {Roo.EventObject} e
4592          */
4593         "click" : true
4594     });
4595 };
4596
4597 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4598     
4599     tag: 'div',
4600     cls: '',
4601     html: '',
4602     preventDefault: false, 
4603     clickable: false,
4604     
4605     getAutoCreate : function(){
4606         
4607         var cfg = {
4608             tag: this.tag,
4609             cls: this.cls,
4610             html: this.html
4611         };
4612         
4613         return cfg;
4614     },
4615     
4616     initEvents: function() 
4617     {
4618         Roo.bootstrap.Element.superclass.initEvents.call(this);
4619         
4620         if(this.clickable){
4621             this.el.on('click', this.onClick, this);
4622         }
4623         
4624     },
4625     
4626     onClick : function(e)
4627     {
4628         if(this.preventDefault){
4629             e.preventDefault();
4630         }
4631         
4632         this.fireEvent('click', this, e);
4633     },
4634     
4635     getValue : function()
4636     {
4637         return this.el.dom.innerHTML;
4638     },
4639     
4640     setValue : function(value)
4641     {
4642         this.el.dom.innerHTML = value;
4643     }
4644    
4645 });
4646
4647  
4648
4649  /*
4650  * - LGPL
4651  *
4652  * pagination
4653  * 
4654  */
4655
4656 /**
4657  * @class Roo.bootstrap.Pagination
4658  * @extends Roo.bootstrap.Component
4659  * Bootstrap Pagination class
4660  * @cfg {String} size xs | sm | md | lg
4661  * @cfg {Boolean} inverse false | true
4662  * 
4663  * @constructor
4664  * Create a new Pagination
4665  * @param {Object} config The config object
4666  */
4667
4668 Roo.bootstrap.Pagination = function(config){
4669     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4670 };
4671
4672 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4673     
4674     cls: false,
4675     size: false,
4676     inverse: false,
4677     
4678     getAutoCreate : function(){
4679         var cfg = {
4680             tag: 'ul',
4681                 cls: 'pagination'
4682         };
4683         if (this.inverse) {
4684             cfg.cls += ' inverse';
4685         }
4686         if (this.html) {
4687             cfg.html=this.html;
4688         }
4689         if (this.cls) {
4690             cfg.cls += " " + this.cls;
4691         }
4692         return cfg;
4693     }
4694    
4695 });
4696
4697  
4698
4699  /*
4700  * - LGPL
4701  *
4702  * Pagination item
4703  * 
4704  */
4705
4706
4707 /**
4708  * @class Roo.bootstrap.PaginationItem
4709  * @extends Roo.bootstrap.Component
4710  * Bootstrap PaginationItem class
4711  * @cfg {String} html text
4712  * @cfg {String} href the link
4713  * @cfg {Boolean} preventDefault (true | false) default true
4714  * @cfg {Boolean} active (true | false) default false
4715  * @cfg {Boolean} disabled default false
4716  * 
4717  * 
4718  * @constructor
4719  * Create a new PaginationItem
4720  * @param {Object} config The config object
4721  */
4722
4723
4724 Roo.bootstrap.PaginationItem = function(config){
4725     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4726     this.addEvents({
4727         // raw events
4728         /**
4729          * @event click
4730          * The raw click event for the entire grid.
4731          * @param {Roo.EventObject} e
4732          */
4733         "click" : true
4734     });
4735 };
4736
4737 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4738     
4739     href : false,
4740     html : false,
4741     preventDefault: true,
4742     active : false,
4743     cls : false,
4744     disabled: false,
4745     
4746     getAutoCreate : function(){
4747         var cfg= {
4748             tag: 'li',
4749             cn: [
4750                 {
4751                     tag : 'a',
4752                     href : this.href ? this.href : '#',
4753                     html : this.html ? this.html : ''
4754                 }
4755             ]
4756         };
4757         
4758         if(this.cls){
4759             cfg.cls = this.cls;
4760         }
4761         
4762         if(this.disabled){
4763             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4764         }
4765         
4766         if(this.active){
4767             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4768         }
4769         
4770         return cfg;
4771     },
4772     
4773     initEvents: function() {
4774         
4775         this.el.on('click', this.onClick, this);
4776         
4777     },
4778     onClick : function(e)
4779     {
4780         Roo.log('PaginationItem on click ');
4781         if(this.preventDefault){
4782             e.preventDefault();
4783         }
4784         
4785         if(this.disabled){
4786             return;
4787         }
4788         
4789         this.fireEvent('click', this, e);
4790     }
4791    
4792 });
4793
4794  
4795
4796  /*
4797  * - LGPL
4798  *
4799  * slider
4800  * 
4801  */
4802
4803
4804 /**
4805  * @class Roo.bootstrap.Slider
4806  * @extends Roo.bootstrap.Component
4807  * Bootstrap Slider class
4808  *    
4809  * @constructor
4810  * Create a new Slider
4811  * @param {Object} config The config object
4812  */
4813
4814 Roo.bootstrap.Slider = function(config){
4815     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4816 };
4817
4818 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4819     
4820     getAutoCreate : function(){
4821         
4822         var cfg = {
4823             tag: 'div',
4824             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4825             cn: [
4826                 {
4827                     tag: 'a',
4828                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4829                 }
4830             ]
4831         };
4832         
4833         return cfg;
4834     }
4835    
4836 });
4837
4838  /*
4839  * Based on:
4840  * Ext JS Library 1.1.1
4841  * Copyright(c) 2006-2007, Ext JS, LLC.
4842  *
4843  * Originally Released Under LGPL - original licence link has changed is not relivant.
4844  *
4845  * Fork - LGPL
4846  * <script type="text/javascript">
4847  */
4848  
4849
4850 /**
4851  * @class Roo.grid.ColumnModel
4852  * @extends Roo.util.Observable
4853  * This is the default implementation of a ColumnModel used by the Grid. It defines
4854  * the columns in the grid.
4855  * <br>Usage:<br>
4856  <pre><code>
4857  var colModel = new Roo.grid.ColumnModel([
4858         {header: "Ticker", width: 60, sortable: true, locked: true},
4859         {header: "Company Name", width: 150, sortable: true},
4860         {header: "Market Cap.", width: 100, sortable: true},
4861         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4862         {header: "Employees", width: 100, sortable: true, resizable: false}
4863  ]);
4864  </code></pre>
4865  * <p>
4866  
4867  * The config options listed for this class are options which may appear in each
4868  * individual column definition.
4869  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4870  * @constructor
4871  * @param {Object} config An Array of column config objects. See this class's
4872  * config objects for details.
4873 */
4874 Roo.grid.ColumnModel = function(config){
4875         /**
4876      * The config passed into the constructor
4877      */
4878     this.config = config;
4879     this.lookup = {};
4880
4881     // if no id, create one
4882     // if the column does not have a dataIndex mapping,
4883     // map it to the order it is in the config
4884     for(var i = 0, len = config.length; i < len; i++){
4885         var c = config[i];
4886         if(typeof c.dataIndex == "undefined"){
4887             c.dataIndex = i;
4888         }
4889         if(typeof c.renderer == "string"){
4890             c.renderer = Roo.util.Format[c.renderer];
4891         }
4892         if(typeof c.id == "undefined"){
4893             c.id = Roo.id();
4894         }
4895         if(c.editor && c.editor.xtype){
4896             c.editor  = Roo.factory(c.editor, Roo.grid);
4897         }
4898         if(c.editor && c.editor.isFormField){
4899             c.editor = new Roo.grid.GridEditor(c.editor);
4900         }
4901         this.lookup[c.id] = c;
4902     }
4903
4904     /**
4905      * The width of columns which have no width specified (defaults to 100)
4906      * @type Number
4907      */
4908     this.defaultWidth = 100;
4909
4910     /**
4911      * Default sortable of columns which have no sortable specified (defaults to false)
4912      * @type Boolean
4913      */
4914     this.defaultSortable = false;
4915
4916     this.addEvents({
4917         /**
4918              * @event widthchange
4919              * Fires when the width of a column changes.
4920              * @param {ColumnModel} this
4921              * @param {Number} columnIndex The column index
4922              * @param {Number} newWidth The new width
4923              */
4924             "widthchange": true,
4925         /**
4926              * @event headerchange
4927              * Fires when the text of a header changes.
4928              * @param {ColumnModel} this
4929              * @param {Number} columnIndex The column index
4930              * @param {Number} newText The new header text
4931              */
4932             "headerchange": true,
4933         /**
4934              * @event hiddenchange
4935              * Fires when a column is hidden or "unhidden".
4936              * @param {ColumnModel} this
4937              * @param {Number} columnIndex The column index
4938              * @param {Boolean} hidden true if hidden, false otherwise
4939              */
4940             "hiddenchange": true,
4941             /**
4942          * @event columnmoved
4943          * Fires when a column is moved.
4944          * @param {ColumnModel} this
4945          * @param {Number} oldIndex
4946          * @param {Number} newIndex
4947          */
4948         "columnmoved" : true,
4949         /**
4950          * @event columlockchange
4951          * Fires when a column's locked state is changed
4952          * @param {ColumnModel} this
4953          * @param {Number} colIndex
4954          * @param {Boolean} locked true if locked
4955          */
4956         "columnlockchange" : true
4957     });
4958     Roo.grid.ColumnModel.superclass.constructor.call(this);
4959 };
4960 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4961     /**
4962      * @cfg {String} header The header text to display in the Grid view.
4963      */
4964     /**
4965      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4966      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4967      * specified, the column's index is used as an index into the Record's data Array.
4968      */
4969     /**
4970      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4971      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4972      */
4973     /**
4974      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4975      * Defaults to the value of the {@link #defaultSortable} property.
4976      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4977      */
4978     /**
4979      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4980      */
4981     /**
4982      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4983      */
4984     /**
4985      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4986      */
4987     /**
4988      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4989      */
4990     /**
4991      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4992      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4993      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4994      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4995      */
4996        /**
4997      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4998      */
4999     /**
5000      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5001      */
5002     /**
5003      * @cfg {String} cursor (Optional)
5004      */
5005     /**
5006      * @cfg {String} tooltip (Optional)
5007      */
5008     /**
5009      * @cfg {Number} xs (Optional)
5010      */
5011     /**
5012      * @cfg {Number} sm (Optional)
5013      */
5014     /**
5015      * @cfg {Number} md (Optional)
5016      */
5017     /**
5018      * @cfg {Number} lg (Optional)
5019      */
5020     /**
5021      * Returns the id of the column at the specified index.
5022      * @param {Number} index The column index
5023      * @return {String} the id
5024      */
5025     getColumnId : function(index){
5026         return this.config[index].id;
5027     },
5028
5029     /**
5030      * Returns the column for a specified id.
5031      * @param {String} id The column id
5032      * @return {Object} the column
5033      */
5034     getColumnById : function(id){
5035         return this.lookup[id];
5036     },
5037
5038     
5039     /**
5040      * Returns the column for a specified dataIndex.
5041      * @param {String} dataIndex The column dataIndex
5042      * @return {Object|Boolean} the column or false if not found
5043      */
5044     getColumnByDataIndex: function(dataIndex){
5045         var index = this.findColumnIndex(dataIndex);
5046         return index > -1 ? this.config[index] : false;
5047     },
5048     
5049     /**
5050      * Returns the index for a specified column id.
5051      * @param {String} id The column id
5052      * @return {Number} the index, or -1 if not found
5053      */
5054     getIndexById : function(id){
5055         for(var i = 0, len = this.config.length; i < len; i++){
5056             if(this.config[i].id == id){
5057                 return i;
5058             }
5059         }
5060         return -1;
5061     },
5062     
5063     /**
5064      * Returns the index for a specified column dataIndex.
5065      * @param {String} dataIndex The column dataIndex
5066      * @return {Number} the index, or -1 if not found
5067      */
5068     
5069     findColumnIndex : function(dataIndex){
5070         for(var i = 0, len = this.config.length; i < len; i++){
5071             if(this.config[i].dataIndex == dataIndex){
5072                 return i;
5073             }
5074         }
5075         return -1;
5076     },
5077     
5078     
5079     moveColumn : function(oldIndex, newIndex){
5080         var c = this.config[oldIndex];
5081         this.config.splice(oldIndex, 1);
5082         this.config.splice(newIndex, 0, c);
5083         this.dataMap = null;
5084         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5085     },
5086
5087     isLocked : function(colIndex){
5088         return this.config[colIndex].locked === true;
5089     },
5090
5091     setLocked : function(colIndex, value, suppressEvent){
5092         if(this.isLocked(colIndex) == value){
5093             return;
5094         }
5095         this.config[colIndex].locked = value;
5096         if(!suppressEvent){
5097             this.fireEvent("columnlockchange", this, colIndex, value);
5098         }
5099     },
5100
5101     getTotalLockedWidth : function(){
5102         var totalWidth = 0;
5103         for(var i = 0; i < this.config.length; i++){
5104             if(this.isLocked(i) && !this.isHidden(i)){
5105                 this.totalWidth += this.getColumnWidth(i);
5106             }
5107         }
5108         return totalWidth;
5109     },
5110
5111     getLockedCount : function(){
5112         for(var i = 0, len = this.config.length; i < len; i++){
5113             if(!this.isLocked(i)){
5114                 return i;
5115             }
5116         }
5117     },
5118
5119     /**
5120      * Returns the number of columns.
5121      * @return {Number}
5122      */
5123     getColumnCount : function(visibleOnly){
5124         if(visibleOnly === true){
5125             var c = 0;
5126             for(var i = 0, len = this.config.length; i < len; i++){
5127                 if(!this.isHidden(i)){
5128                     c++;
5129                 }
5130             }
5131             return c;
5132         }
5133         return this.config.length;
5134     },
5135
5136     /**
5137      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5138      * @param {Function} fn
5139      * @param {Object} scope (optional)
5140      * @return {Array} result
5141      */
5142     getColumnsBy : function(fn, scope){
5143         var r = [];
5144         for(var i = 0, len = this.config.length; i < len; i++){
5145             var c = this.config[i];
5146             if(fn.call(scope||this, c, i) === true){
5147                 r[r.length] = c;
5148             }
5149         }
5150         return r;
5151     },
5152
5153     /**
5154      * Returns true if the specified column is sortable.
5155      * @param {Number} col The column index
5156      * @return {Boolean}
5157      */
5158     isSortable : function(col){
5159         if(typeof this.config[col].sortable == "undefined"){
5160             return this.defaultSortable;
5161         }
5162         return this.config[col].sortable;
5163     },
5164
5165     /**
5166      * Returns the rendering (formatting) function defined for the column.
5167      * @param {Number} col The column index.
5168      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5169      */
5170     getRenderer : function(col){
5171         if(!this.config[col].renderer){
5172             return Roo.grid.ColumnModel.defaultRenderer;
5173         }
5174         return this.config[col].renderer;
5175     },
5176
5177     /**
5178      * Sets the rendering (formatting) function for a column.
5179      * @param {Number} col The column index
5180      * @param {Function} fn The function to use to process the cell's raw data
5181      * to return HTML markup for the grid view. The render function is called with
5182      * the following parameters:<ul>
5183      * <li>Data value.</li>
5184      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5185      * <li>css A CSS style string to apply to the table cell.</li>
5186      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5187      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5188      * <li>Row index</li>
5189      * <li>Column index</li>
5190      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5191      */
5192     setRenderer : function(col, fn){
5193         this.config[col].renderer = fn;
5194     },
5195
5196     /**
5197      * Returns the width for the specified column.
5198      * @param {Number} col The column index
5199      * @return {Number}
5200      */
5201     getColumnWidth : function(col){
5202         return this.config[col].width * 1 || this.defaultWidth;
5203     },
5204
5205     /**
5206      * Sets the width for a column.
5207      * @param {Number} col The column index
5208      * @param {Number} width The new width
5209      */
5210     setColumnWidth : function(col, width, suppressEvent){
5211         this.config[col].width = width;
5212         this.totalWidth = null;
5213         if(!suppressEvent){
5214              this.fireEvent("widthchange", this, col, width);
5215         }
5216     },
5217
5218     /**
5219      * Returns the total width of all columns.
5220      * @param {Boolean} includeHidden True to include hidden column widths
5221      * @return {Number}
5222      */
5223     getTotalWidth : function(includeHidden){
5224         if(!this.totalWidth){
5225             this.totalWidth = 0;
5226             for(var i = 0, len = this.config.length; i < len; i++){
5227                 if(includeHidden || !this.isHidden(i)){
5228                     this.totalWidth += this.getColumnWidth(i);
5229                 }
5230             }
5231         }
5232         return this.totalWidth;
5233     },
5234
5235     /**
5236      * Returns the header for the specified column.
5237      * @param {Number} col The column index
5238      * @return {String}
5239      */
5240     getColumnHeader : function(col){
5241         return this.config[col].header;
5242     },
5243
5244     /**
5245      * Sets the header for a column.
5246      * @param {Number} col The column index
5247      * @param {String} header The new header
5248      */
5249     setColumnHeader : function(col, header){
5250         this.config[col].header = header;
5251         this.fireEvent("headerchange", this, col, header);
5252     },
5253
5254     /**
5255      * Returns the tooltip for the specified column.
5256      * @param {Number} col The column index
5257      * @return {String}
5258      */
5259     getColumnTooltip : function(col){
5260             return this.config[col].tooltip;
5261     },
5262     /**
5263      * Sets the tooltip for a column.
5264      * @param {Number} col The column index
5265      * @param {String} tooltip The new tooltip
5266      */
5267     setColumnTooltip : function(col, tooltip){
5268             this.config[col].tooltip = tooltip;
5269     },
5270
5271     /**
5272      * Returns the dataIndex for the specified column.
5273      * @param {Number} col The column index
5274      * @return {Number}
5275      */
5276     getDataIndex : function(col){
5277         return this.config[col].dataIndex;
5278     },
5279
5280     /**
5281      * Sets the dataIndex for a column.
5282      * @param {Number} col The column index
5283      * @param {Number} dataIndex The new dataIndex
5284      */
5285     setDataIndex : function(col, dataIndex){
5286         this.config[col].dataIndex = dataIndex;
5287     },
5288
5289     
5290     
5291     /**
5292      * Returns true if the cell is editable.
5293      * @param {Number} colIndex The column index
5294      * @param {Number} rowIndex The row index
5295      * @return {Boolean}
5296      */
5297     isCellEditable : function(colIndex, rowIndex){
5298         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5299     },
5300
5301     /**
5302      * Returns the editor defined for the cell/column.
5303      * return false or null to disable editing.
5304      * @param {Number} colIndex The column index
5305      * @param {Number} rowIndex The row index
5306      * @return {Object}
5307      */
5308     getCellEditor : function(colIndex, rowIndex){
5309         return this.config[colIndex].editor;
5310     },
5311
5312     /**
5313      * Sets if a column is editable.
5314      * @param {Number} col The column index
5315      * @param {Boolean} editable True if the column is editable
5316      */
5317     setEditable : function(col, editable){
5318         this.config[col].editable = editable;
5319     },
5320
5321
5322     /**
5323      * Returns true if the column is hidden.
5324      * @param {Number} colIndex The column index
5325      * @return {Boolean}
5326      */
5327     isHidden : function(colIndex){
5328         return this.config[colIndex].hidden;
5329     },
5330
5331
5332     /**
5333      * Returns true if the column width cannot be changed
5334      */
5335     isFixed : function(colIndex){
5336         return this.config[colIndex].fixed;
5337     },
5338
5339     /**
5340      * Returns true if the column can be resized
5341      * @return {Boolean}
5342      */
5343     isResizable : function(colIndex){
5344         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5345     },
5346     /**
5347      * Sets if a column is hidden.
5348      * @param {Number} colIndex The column index
5349      * @param {Boolean} hidden True if the column is hidden
5350      */
5351     setHidden : function(colIndex, hidden){
5352         this.config[colIndex].hidden = hidden;
5353         this.totalWidth = null;
5354         this.fireEvent("hiddenchange", this, colIndex, hidden);
5355     },
5356
5357     /**
5358      * Sets the editor for a column.
5359      * @param {Number} col The column index
5360      * @param {Object} editor The editor object
5361      */
5362     setEditor : function(col, editor){
5363         this.config[col].editor = editor;
5364     }
5365 });
5366
5367 Roo.grid.ColumnModel.defaultRenderer = function(value){
5368         if(typeof value == "string" && value.length < 1){
5369             return "&#160;";
5370         }
5371         return value;
5372 };
5373
5374 // Alias for backwards compatibility
5375 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5376 /*
5377  * Based on:
5378  * Ext JS Library 1.1.1
5379  * Copyright(c) 2006-2007, Ext JS, LLC.
5380  *
5381  * Originally Released Under LGPL - original licence link has changed is not relivant.
5382  *
5383  * Fork - LGPL
5384  * <script type="text/javascript">
5385  */
5386  
5387 /**
5388  * @class Roo.LoadMask
5389  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5390  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5391  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5392  * element's UpdateManager load indicator and will be destroyed after the initial load.
5393  * @constructor
5394  * Create a new LoadMask
5395  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5396  * @param {Object} config The config object
5397  */
5398 Roo.LoadMask = function(el, config){
5399     this.el = Roo.get(el);
5400     Roo.apply(this, config);
5401     if(this.store){
5402         this.store.on('beforeload', this.onBeforeLoad, this);
5403         this.store.on('load', this.onLoad, this);
5404         this.store.on('loadexception', this.onLoadException, this);
5405         this.removeMask = false;
5406     }else{
5407         var um = this.el.getUpdateManager();
5408         um.showLoadIndicator = false; // disable the default indicator
5409         um.on('beforeupdate', this.onBeforeLoad, this);
5410         um.on('update', this.onLoad, this);
5411         um.on('failure', this.onLoad, this);
5412         this.removeMask = true;
5413     }
5414 };
5415
5416 Roo.LoadMask.prototype = {
5417     /**
5418      * @cfg {Boolean} removeMask
5419      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5420      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5421      */
5422     /**
5423      * @cfg {String} msg
5424      * The text to display in a centered loading message box (defaults to 'Loading...')
5425      */
5426     msg : 'Loading...',
5427     /**
5428      * @cfg {String} msgCls
5429      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5430      */
5431     msgCls : 'x-mask-loading',
5432
5433     /**
5434      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5435      * @type Boolean
5436      */
5437     disabled: false,
5438
5439     /**
5440      * Disables the mask to prevent it from being displayed
5441      */
5442     disable : function(){
5443        this.disabled = true;
5444     },
5445
5446     /**
5447      * Enables the mask so that it can be displayed
5448      */
5449     enable : function(){
5450         this.disabled = false;
5451     },
5452     
5453     onLoadException : function()
5454     {
5455         Roo.log(arguments);
5456         
5457         if (typeof(arguments[3]) != 'undefined') {
5458             Roo.MessageBox.alert("Error loading",arguments[3]);
5459         } 
5460         /*
5461         try {
5462             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5463                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5464             }   
5465         } catch(e) {
5466             
5467         }
5468         */
5469     
5470         
5471         
5472         this.el.unmask(this.removeMask);
5473     },
5474     // private
5475     onLoad : function()
5476     {
5477         this.el.unmask(this.removeMask);
5478     },
5479
5480     // private
5481     onBeforeLoad : function(){
5482         if(!this.disabled){
5483             this.el.mask(this.msg, this.msgCls);
5484         }
5485     },
5486
5487     // private
5488     destroy : function(){
5489         if(this.store){
5490             this.store.un('beforeload', this.onBeforeLoad, this);
5491             this.store.un('load', this.onLoad, this);
5492             this.store.un('loadexception', this.onLoadException, this);
5493         }else{
5494             var um = this.el.getUpdateManager();
5495             um.un('beforeupdate', this.onBeforeLoad, this);
5496             um.un('update', this.onLoad, this);
5497             um.un('failure', this.onLoad, this);
5498         }
5499     }
5500 };/*
5501  * - LGPL
5502  *
5503  * table
5504  * 
5505  */
5506
5507 /**
5508  * @class Roo.bootstrap.Table
5509  * @extends Roo.bootstrap.Component
5510  * Bootstrap Table class
5511  * @cfg {String} cls table class
5512  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5513  * @cfg {String} bgcolor Specifies the background color for a table
5514  * @cfg {Number} border Specifies whether the table cells should have borders or not
5515  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5516  * @cfg {Number} cellspacing Specifies the space between cells
5517  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5518  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5519  * @cfg {String} sortable Specifies that the table should be sortable
5520  * @cfg {String} summary Specifies a summary of the content of a table
5521  * @cfg {Number} width Specifies the width of a table
5522  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5523  * 
5524  * @cfg {boolean} striped Should the rows be alternative striped
5525  * @cfg {boolean} bordered Add borders to the table
5526  * @cfg {boolean} hover Add hover highlighting
5527  * @cfg {boolean} condensed Format condensed
5528  * @cfg {boolean} responsive Format condensed
5529  * @cfg {Boolean} loadMask (true|false) default false
5530  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5531  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5532  * @cfg {Boolean} rowSelection (true|false) default false
5533  * @cfg {Boolean} cellSelection (true|false) default false
5534  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5535  
5536  * 
5537  * @constructor
5538  * Create a new Table
5539  * @param {Object} config The config object
5540  */
5541
5542 Roo.bootstrap.Table = function(config){
5543     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5544     
5545     // BC...
5546     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5547     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5548     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5549     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5550     
5551     
5552     if (this.sm) {
5553         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5554         this.sm = this.selModel;
5555         this.sm.xmodule = this.xmodule || false;
5556     }
5557     if (this.cm && typeof(this.cm.config) == 'undefined') {
5558         this.colModel = new Roo.grid.ColumnModel(this.cm);
5559         this.cm = this.colModel;
5560         this.cm.xmodule = this.xmodule || false;
5561     }
5562     if (this.store) {
5563         this.store= Roo.factory(this.store, Roo.data);
5564         this.ds = this.store;
5565         this.ds.xmodule = this.xmodule || false;
5566          
5567     }
5568     if (this.footer && this.store) {
5569         this.footer.dataSource = this.ds;
5570         this.footer = Roo.factory(this.footer);
5571     }
5572     
5573     /** @private */
5574     this.addEvents({
5575         /**
5576          * @event cellclick
5577          * Fires when a cell is clicked
5578          * @param {Roo.bootstrap.Table} this
5579          * @param {Roo.Element} el
5580          * @param {Number} rowIndex
5581          * @param {Number} columnIndex
5582          * @param {Roo.EventObject} e
5583          */
5584         "cellclick" : true,
5585         /**
5586          * @event celldblclick
5587          * Fires when a cell is double clicked
5588          * @param {Roo.bootstrap.Table} this
5589          * @param {Roo.Element} el
5590          * @param {Number} rowIndex
5591          * @param {Number} columnIndex
5592          * @param {Roo.EventObject} e
5593          */
5594         "celldblclick" : true,
5595         /**
5596          * @event rowclick
5597          * Fires when a row is clicked
5598          * @param {Roo.bootstrap.Table} this
5599          * @param {Roo.Element} el
5600          * @param {Number} rowIndex
5601          * @param {Roo.EventObject} e
5602          */
5603         "rowclick" : true,
5604         /**
5605          * @event rowdblclick
5606          * Fires when a row is double clicked
5607          * @param {Roo.bootstrap.Table} this
5608          * @param {Roo.Element} el
5609          * @param {Number} rowIndex
5610          * @param {Roo.EventObject} e
5611          */
5612         "rowdblclick" : true,
5613         /**
5614          * @event mouseover
5615          * Fires when a mouseover occur
5616          * @param {Roo.bootstrap.Table} this
5617          * @param {Roo.Element} el
5618          * @param {Number} rowIndex
5619          * @param {Number} columnIndex
5620          * @param {Roo.EventObject} e
5621          */
5622         "mouseover" : true,
5623         /**
5624          * @event mouseout
5625          * Fires when a mouseout occur
5626          * @param {Roo.bootstrap.Table} this
5627          * @param {Roo.Element} el
5628          * @param {Number} rowIndex
5629          * @param {Number} columnIndex
5630          * @param {Roo.EventObject} e
5631          */
5632         "mouseout" : true,
5633         /**
5634          * @event rowclass
5635          * Fires when a row is rendered, so you can change add a style to it.
5636          * @param {Roo.bootstrap.Table} this
5637          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5638          */
5639         'rowclass' : true,
5640           /**
5641          * @event rowsrendered
5642          * Fires when all the  rows have been rendered
5643          * @param {Roo.bootstrap.Table} this
5644          */
5645         'rowsrendered' : true
5646         
5647     });
5648 };
5649
5650 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5651     
5652     cls: false,
5653     align: false,
5654     bgcolor: false,
5655     border: false,
5656     cellpadding: false,
5657     cellspacing: false,
5658     frame: false,
5659     rules: false,
5660     sortable: false,
5661     summary: false,
5662     width: false,
5663     striped : false,
5664     bordered: false,
5665     hover:  false,
5666     condensed : false,
5667     responsive : false,
5668     sm : false,
5669     cm : false,
5670     store : false,
5671     loadMask : false,
5672     footerShow : true,
5673     headerShow : true,
5674   
5675     rowSelection : false,
5676     cellSelection : false,
5677     layout : false,
5678     
5679     // Roo.Element - the tbody
5680     mainBody: false, 
5681     
5682     getAutoCreate : function(){
5683         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5684         
5685         cfg = {
5686             tag: 'table',
5687             cls : 'table',
5688             cn : []
5689         };
5690             
5691         if (this.striped) {
5692             cfg.cls += ' table-striped';
5693         }
5694         
5695         if (this.hover) {
5696             cfg.cls += ' table-hover';
5697         }
5698         if (this.bordered) {
5699             cfg.cls += ' table-bordered';
5700         }
5701         if (this.condensed) {
5702             cfg.cls += ' table-condensed';
5703         }
5704         if (this.responsive) {
5705             cfg.cls += ' table-responsive';
5706         }
5707         
5708         if (this.cls) {
5709             cfg.cls+=  ' ' +this.cls;
5710         }
5711         
5712         // this lot should be simplifed...
5713         
5714         if (this.align) {
5715             cfg.align=this.align;
5716         }
5717         if (this.bgcolor) {
5718             cfg.bgcolor=this.bgcolor;
5719         }
5720         if (this.border) {
5721             cfg.border=this.border;
5722         }
5723         if (this.cellpadding) {
5724             cfg.cellpadding=this.cellpadding;
5725         }
5726         if (this.cellspacing) {
5727             cfg.cellspacing=this.cellspacing;
5728         }
5729         if (this.frame) {
5730             cfg.frame=this.frame;
5731         }
5732         if (this.rules) {
5733             cfg.rules=this.rules;
5734         }
5735         if (this.sortable) {
5736             cfg.sortable=this.sortable;
5737         }
5738         if (this.summary) {
5739             cfg.summary=this.summary;
5740         }
5741         if (this.width) {
5742             cfg.width=this.width;
5743         }
5744         if (this.layout) {
5745             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5746         }
5747         
5748         if(this.store || this.cm){
5749             if(this.headerShow){
5750                 cfg.cn.push(this.renderHeader());
5751             }
5752             
5753             cfg.cn.push(this.renderBody());
5754             
5755             if(this.footerShow){
5756                 cfg.cn.push(this.renderFooter());
5757             }
5758             
5759             cfg.cls+=  ' TableGrid';
5760         }
5761         
5762         return { cn : [ cfg ] };
5763     },
5764     
5765     initEvents : function()
5766     {   
5767         if(!this.store || !this.cm){
5768             return;
5769         }
5770         
5771         //Roo.log('initEvents with ds!!!!');
5772         
5773         this.mainBody = this.el.select('tbody', true).first();
5774         
5775         
5776         var _this = this;
5777         
5778         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5779             e.on('click', _this.sort, _this);
5780         });
5781         
5782         this.el.on("click", this.onClick, this);
5783         this.el.on("dblclick", this.onDblClick, this);
5784         
5785         // why is this done????? = it breaks dialogs??
5786         //this.parent().el.setStyle('position', 'relative');
5787         
5788         
5789         if (this.footer) {
5790             this.footer.parentId = this.id;
5791             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5792         }
5793         
5794         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5795         
5796         this.store.on('load', this.onLoad, this);
5797         this.store.on('beforeload', this.onBeforeLoad, this);
5798         this.store.on('update', this.onUpdate, this);
5799         this.store.on('add', this.onAdd, this);
5800         
5801     },
5802     
5803     onMouseover : function(e, el)
5804     {
5805         var cell = Roo.get(el);
5806         
5807         if(!cell){
5808             return;
5809         }
5810         
5811         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5812             cell = cell.findParent('td', false, true);
5813         }
5814         
5815         var row = cell.findParent('tr', false, true);
5816         var cellIndex = cell.dom.cellIndex;
5817         var rowIndex = row.dom.rowIndex - 1; // start from 0
5818         
5819         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5820         
5821     },
5822     
5823     onMouseout : function(e, el)
5824     {
5825         var cell = Roo.get(el);
5826         
5827         if(!cell){
5828             return;
5829         }
5830         
5831         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5832             cell = cell.findParent('td', false, true);
5833         }
5834         
5835         var row = cell.findParent('tr', false, true);
5836         var cellIndex = cell.dom.cellIndex;
5837         var rowIndex = row.dom.rowIndex - 1; // start from 0
5838         
5839         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5840         
5841     },
5842     
5843     onClick : function(e, el)
5844     {
5845         var cell = Roo.get(el);
5846         
5847         if(!cell || (!this.cellSelection && !this.rowSelection)){
5848             return;
5849         }
5850         
5851         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5852             cell = cell.findParent('td', false, true);
5853         }
5854         
5855         if(!cell || typeof(cell) == 'undefined'){
5856             return;
5857         }
5858         
5859         var row = cell.findParent('tr', false, true);
5860         
5861         if(!row || typeof(row) == 'undefined'){
5862             return;
5863         }
5864         
5865         var cellIndex = cell.dom.cellIndex;
5866         var rowIndex = this.getRowIndex(row);
5867         
5868         // why??? - should these not be based on SelectionModel?
5869         if(this.cellSelection){
5870             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5871         }
5872         
5873         if(this.rowSelection){
5874             this.fireEvent('rowclick', this, row, rowIndex, e);
5875         }
5876         
5877         
5878     },
5879     
5880     onDblClick : function(e,el)
5881     {
5882         var cell = Roo.get(el);
5883         
5884         if(!cell || (!this.CellSelection && !this.RowSelection)){
5885             return;
5886         }
5887         
5888         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5889             cell = cell.findParent('td', false, true);
5890         }
5891         
5892         if(!cell || typeof(cell) == 'undefined'){
5893             return;
5894         }
5895         
5896         var row = cell.findParent('tr', false, true);
5897         
5898         if(!row || typeof(row) == 'undefined'){
5899             return;
5900         }
5901         
5902         var cellIndex = cell.dom.cellIndex;
5903         var rowIndex = this.getRowIndex(row);
5904         
5905         if(this.CellSelection){
5906             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5907         }
5908         
5909         if(this.RowSelection){
5910             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5911         }
5912     },
5913     
5914     sort : function(e,el)
5915     {
5916         var col = Roo.get(el);
5917         
5918         if(!col.hasClass('sortable')){
5919             return;
5920         }
5921         
5922         var sort = col.attr('sort');
5923         var dir = 'ASC';
5924         
5925         if(col.hasClass('glyphicon-arrow-up')){
5926             dir = 'DESC';
5927         }
5928         
5929         this.store.sortInfo = {field : sort, direction : dir};
5930         
5931         if (this.footer) {
5932             Roo.log("calling footer first");
5933             this.footer.onClick('first');
5934         } else {
5935         
5936             this.store.load({ params : { start : 0 } });
5937         }
5938     },
5939     
5940     renderHeader : function()
5941     {
5942         var header = {
5943             tag: 'thead',
5944             cn : []
5945         };
5946         
5947         var cm = this.cm;
5948         
5949         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5950             
5951             var config = cm.config[i];
5952             
5953             var c = {
5954                 tag: 'th',
5955                 style : '',
5956                 html: cm.getColumnHeader(i)
5957             };
5958             
5959             var hh = '';
5960             
5961             if(typeof(config.lgHeader) != 'undefined'){
5962                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5963             }
5964             
5965             if(typeof(config.mdHeader) != 'undefined'){
5966                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5967             }
5968             
5969             if(typeof(config.smHeader) != 'undefined'){
5970                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5971             }
5972             
5973             if(typeof(config.xsHeader) != 'undefined'){
5974                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5975             }
5976             
5977             if(hh.length){
5978                 c.html = hh;
5979             }
5980             
5981             if(typeof(config.tooltip) != 'undefined'){
5982                 c.tooltip = config.tooltip;
5983             }
5984             
5985             if(typeof(config.colspan) != 'undefined'){
5986                 c.colspan = config.colspan;
5987             }
5988             
5989             if(typeof(config.hidden) != 'undefined' && config.hidden){
5990                 c.style += ' display:none;';
5991             }
5992             
5993             if(typeof(config.dataIndex) != 'undefined'){
5994                 c.sort = config.dataIndex;
5995             }
5996             
5997             if(typeof(config.sortable) != 'undefined' && config.sortable){
5998                 c.cls = 'sortable';
5999             }
6000             
6001             if(typeof(config.align) != 'undefined' && config.align.length){
6002                 c.style += ' text-align:' + config.align + ';';
6003             }
6004             
6005             if(typeof(config.width) != 'undefined'){
6006                 c.style += ' width:' + config.width + 'px;';
6007             }
6008             
6009             if(typeof(config.cls) != 'undefined'){
6010                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6011             }
6012             
6013             ['xs','sm','md','lg'].map(function(size){
6014                 
6015                 if(typeof(config[size]) == 'undefined'){
6016                     return;
6017                 }
6018                 
6019                 if (!config[size]) { // 0 = hidden
6020                     cfg.cls += ' hidden-' + size;
6021                     return;
6022                 }
6023                 
6024                 cfg.cls += ' col-' + size + '-' + config[size];
6025
6026             });
6027             
6028             header.cn.push(c)
6029         }
6030         
6031         return header;
6032     },
6033     
6034     renderBody : function()
6035     {
6036         var body = {
6037             tag: 'tbody',
6038             cn : [
6039                 {
6040                     tag: 'tr',
6041                     cn : [
6042                         {
6043                             tag : 'td',
6044                             colspan :  this.cm.getColumnCount()
6045                         }
6046                     ]
6047                 }
6048             ]
6049         };
6050         
6051         return body;
6052     },
6053     
6054     renderFooter : function()
6055     {
6056         var footer = {
6057             tag: 'tfoot',
6058             cn : [
6059                 {
6060                     tag: 'tr',
6061                     cn : [
6062                         {
6063                             tag : 'td',
6064                             colspan :  this.cm.getColumnCount()
6065                         }
6066                     ]
6067                 }
6068             ]
6069         };
6070         
6071         return footer;
6072     },
6073     
6074     
6075     
6076     onLoad : function()
6077     {
6078         Roo.log('ds onload');
6079         this.clear();
6080         
6081         var _this = this;
6082         var cm = this.cm;
6083         var ds = this.store;
6084         
6085         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6086             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6087             
6088             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6089                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6090             }
6091             
6092             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6093                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6094             }
6095         });
6096         
6097         var tbody =  this.mainBody;
6098               
6099         if(ds.getCount() > 0){
6100             ds.data.each(function(d,rowIndex){
6101                 var row =  this.renderRow(cm, ds, rowIndex);
6102                 
6103                 tbody.createChild(row);
6104                 
6105                 var _this = this;
6106                 
6107                 if(row.cellObjects.length){
6108                     Roo.each(row.cellObjects, function(r){
6109                         _this.renderCellObject(r);
6110                     })
6111                 }
6112                 
6113             }, this);
6114         }
6115         
6116         Roo.each(this.el.select('tbody td', true).elements, function(e){
6117             e.on('mouseover', _this.onMouseover, _this);
6118         });
6119         
6120         Roo.each(this.el.select('tbody td', true).elements, function(e){
6121             e.on('mouseout', _this.onMouseout, _this);
6122         });
6123         this.fireEvent('rowsrendered', this);
6124         //if(this.loadMask){
6125         //    this.maskEl.hide();
6126         //}
6127     },
6128     
6129     
6130     onUpdate : function(ds,record)
6131     {
6132         this.refreshRow(record);
6133     },
6134     
6135     onRemove : function(ds, record, index, isUpdate){
6136         if(isUpdate !== true){
6137             this.fireEvent("beforerowremoved", this, index, record);
6138         }
6139         var bt = this.mainBody.dom;
6140         
6141         var rows = this.el.select('tbody > tr', true).elements;
6142         
6143         if(typeof(rows[index]) != 'undefined'){
6144             bt.removeChild(rows[index].dom);
6145         }
6146         
6147 //        if(bt.rows[index]){
6148 //            bt.removeChild(bt.rows[index]);
6149 //        }
6150         
6151         if(isUpdate !== true){
6152             //this.stripeRows(index);
6153             //this.syncRowHeights(index, index);
6154             //this.layout();
6155             this.fireEvent("rowremoved", this, index, record);
6156         }
6157     },
6158     
6159     onAdd : function(ds, records, rowIndex)
6160     {
6161         //Roo.log('on Add called');
6162         // - note this does not handle multiple adding very well..
6163         var bt = this.mainBody.dom;
6164         for (var i =0 ; i < records.length;i++) {
6165             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6166             //Roo.log(records[i]);
6167             //Roo.log(this.store.getAt(rowIndex+i));
6168             this.insertRow(this.store, rowIndex + i, false);
6169             return;
6170         }
6171         
6172     },
6173     
6174     
6175     refreshRow : function(record){
6176         var ds = this.store, index;
6177         if(typeof record == 'number'){
6178             index = record;
6179             record = ds.getAt(index);
6180         }else{
6181             index = ds.indexOf(record);
6182         }
6183         this.insertRow(ds, index, true);
6184         this.onRemove(ds, record, index+1, true);
6185         //this.syncRowHeights(index, index);
6186         //this.layout();
6187         this.fireEvent("rowupdated", this, index, record);
6188     },
6189     
6190     insertRow : function(dm, rowIndex, isUpdate){
6191         
6192         if(!isUpdate){
6193             this.fireEvent("beforerowsinserted", this, rowIndex);
6194         }
6195             //var s = this.getScrollState();
6196         var row = this.renderRow(this.cm, this.store, rowIndex);
6197         // insert before rowIndex..
6198         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6199         
6200         var _this = this;
6201                 
6202         if(row.cellObjects.length){
6203             Roo.each(row.cellObjects, function(r){
6204                 _this.renderCellObject(r);
6205             })
6206         }
6207             
6208         if(!isUpdate){
6209             this.fireEvent("rowsinserted", this, rowIndex);
6210             //this.syncRowHeights(firstRow, lastRow);
6211             //this.stripeRows(firstRow);
6212             //this.layout();
6213         }
6214         
6215     },
6216     
6217     
6218     getRowDom : function(rowIndex)
6219     {
6220         var rows = this.el.select('tbody > tr', true).elements;
6221         
6222         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6223         
6224     },
6225     // returns the object tree for a tr..
6226   
6227     
6228     renderRow : function(cm, ds, rowIndex) 
6229     {
6230         
6231         var d = ds.getAt(rowIndex);
6232         
6233         var row = {
6234             tag : 'tr',
6235             cn : []
6236         };
6237             
6238         var cellObjects = [];
6239         
6240         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6241             var config = cm.config[i];
6242             
6243             var renderer = cm.getRenderer(i);
6244             var value = '';
6245             var id = false;
6246             
6247             if(typeof(renderer) !== 'undefined'){
6248                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6249             }
6250             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6251             // and are rendered into the cells after the row is rendered - using the id for the element.
6252             
6253             if(typeof(value) === 'object'){
6254                 id = Roo.id();
6255                 cellObjects.push({
6256                     container : id,
6257                     cfg : value 
6258                 })
6259             }
6260             
6261             var rowcfg = {
6262                 record: d,
6263                 rowIndex : rowIndex,
6264                 colIndex : i,
6265                 rowClass : ''
6266             };
6267
6268             this.fireEvent('rowclass', this, rowcfg);
6269             
6270             var td = {
6271                 tag: 'td',
6272                 cls : rowcfg.rowClass,
6273                 style: '',
6274                 html: (typeof(value) === 'object') ? '' : value
6275             };
6276             
6277             if (id) {
6278                 td.id = id;
6279             }
6280             
6281             if(typeof(config.colspan) != 'undefined'){
6282                 td.colspan = config.colspan;
6283             }
6284             
6285             if(typeof(config.hidden) != 'undefined' && config.hidden){
6286                 td.style += ' display:none;';
6287             }
6288             
6289             if(typeof(config.align) != 'undefined' && config.align.length){
6290                 td.style += ' text-align:' + config.align + ';';
6291             }
6292             
6293             if(typeof(config.width) != 'undefined'){
6294                 td.style += ' width:' +  config.width + 'px;';
6295             }
6296             
6297             if(typeof(config.cursor) != 'undefined'){
6298                 td.style += ' cursor:' +  config.cursor + ';';
6299             }
6300             
6301             if(typeof(config.cls) != 'undefined'){
6302                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6303             }
6304             
6305             ['xs','sm','md','lg'].map(function(size){
6306                 
6307                 if(typeof(config[size]) == 'undefined'){
6308                     return;
6309                 }
6310                 
6311                 if (!config[size]) { // 0 = hidden
6312                     td.cls += ' hidden-' + size;
6313                     return;
6314                 }
6315                 
6316                 td.cls += ' col-' + size + '-' + config[size];
6317
6318             });
6319              
6320             row.cn.push(td);
6321            
6322         }
6323         
6324         row.cellObjects = cellObjects;
6325         
6326         return row;
6327           
6328     },
6329     
6330     
6331     
6332     onBeforeLoad : function()
6333     {
6334         //Roo.log('ds onBeforeLoad');
6335         
6336         //this.clear();
6337         
6338         //if(this.loadMask){
6339         //    this.maskEl.show();
6340         //}
6341     },
6342      /**
6343      * Remove all rows
6344      */
6345     clear : function()
6346     {
6347         this.el.select('tbody', true).first().dom.innerHTML = '';
6348     },
6349     /**
6350      * Show or hide a row.
6351      * @param {Number} rowIndex to show or hide
6352      * @param {Boolean} state hide
6353      */
6354     setRowVisibility : function(rowIndex, state)
6355     {
6356         var bt = this.mainBody.dom;
6357         
6358         var rows = this.el.select('tbody > tr', true).elements;
6359         
6360         if(typeof(rows[rowIndex]) == 'undefined'){
6361             return;
6362         }
6363         rows[rowIndex].dom.style.display = state ? '' : 'none';
6364     },
6365     
6366     
6367     getSelectionModel : function(){
6368         if(!this.selModel){
6369             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6370         }
6371         return this.selModel;
6372     },
6373     /*
6374      * Render the Roo.bootstrap object from renderder
6375      */
6376     renderCellObject : function(r)
6377     {
6378         var _this = this;
6379         
6380         var t = r.cfg.render(r.container);
6381         
6382         if(r.cfg.cn){
6383             Roo.each(r.cfg.cn, function(c){
6384                 var child = {
6385                     container: t.getChildContainer(),
6386                     cfg: c
6387                 };
6388                 _this.renderCellObject(child);
6389             })
6390         }
6391     },
6392     
6393     getRowIndex : function(row)
6394     {
6395         var rowIndex = -1;
6396         
6397         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6398             if(el != row){
6399                 return;
6400             }
6401             
6402             rowIndex = index;
6403         });
6404         
6405         return rowIndex;
6406     }
6407    
6408 });
6409
6410  
6411
6412  /*
6413  * - LGPL
6414  *
6415  * table cell
6416  * 
6417  */
6418
6419 /**
6420  * @class Roo.bootstrap.TableCell
6421  * @extends Roo.bootstrap.Component
6422  * Bootstrap TableCell class
6423  * @cfg {String} html cell contain text
6424  * @cfg {String} cls cell class
6425  * @cfg {String} tag cell tag (td|th) default td
6426  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6427  * @cfg {String} align Aligns the content in a cell
6428  * @cfg {String} axis Categorizes cells
6429  * @cfg {String} bgcolor Specifies the background color of a cell
6430  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6431  * @cfg {Number} colspan Specifies the number of columns a cell should span
6432  * @cfg {String} headers Specifies one or more header cells a cell is related to
6433  * @cfg {Number} height Sets the height of a cell
6434  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6435  * @cfg {Number} rowspan Sets the number of rows a cell should span
6436  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6437  * @cfg {String} valign Vertical aligns the content in a cell
6438  * @cfg {Number} width Specifies the width of a cell
6439  * 
6440  * @constructor
6441  * Create a new TableCell
6442  * @param {Object} config The config object
6443  */
6444
6445 Roo.bootstrap.TableCell = function(config){
6446     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6447 };
6448
6449 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6450     
6451     html: false,
6452     cls: false,
6453     tag: false,
6454     abbr: false,
6455     align: false,
6456     axis: false,
6457     bgcolor: false,
6458     charoff: false,
6459     colspan: false,
6460     headers: false,
6461     height: false,
6462     nowrap: false,
6463     rowspan: false,
6464     scope: false,
6465     valign: false,
6466     width: false,
6467     
6468     
6469     getAutoCreate : function(){
6470         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6471         
6472         cfg = {
6473             tag: 'td'
6474         };
6475         
6476         if(this.tag){
6477             cfg.tag = this.tag;
6478         }
6479         
6480         if (this.html) {
6481             cfg.html=this.html
6482         }
6483         if (this.cls) {
6484             cfg.cls=this.cls
6485         }
6486         if (this.abbr) {
6487             cfg.abbr=this.abbr
6488         }
6489         if (this.align) {
6490             cfg.align=this.align
6491         }
6492         if (this.axis) {
6493             cfg.axis=this.axis
6494         }
6495         if (this.bgcolor) {
6496             cfg.bgcolor=this.bgcolor
6497         }
6498         if (this.charoff) {
6499             cfg.charoff=this.charoff
6500         }
6501         if (this.colspan) {
6502             cfg.colspan=this.colspan
6503         }
6504         if (this.headers) {
6505             cfg.headers=this.headers
6506         }
6507         if (this.height) {
6508             cfg.height=this.height
6509         }
6510         if (this.nowrap) {
6511             cfg.nowrap=this.nowrap
6512         }
6513         if (this.rowspan) {
6514             cfg.rowspan=this.rowspan
6515         }
6516         if (this.scope) {
6517             cfg.scope=this.scope
6518         }
6519         if (this.valign) {
6520             cfg.valign=this.valign
6521         }
6522         if (this.width) {
6523             cfg.width=this.width
6524         }
6525         
6526         
6527         return cfg;
6528     }
6529    
6530 });
6531
6532  
6533
6534  /*
6535  * - LGPL
6536  *
6537  * table row
6538  * 
6539  */
6540
6541 /**
6542  * @class Roo.bootstrap.TableRow
6543  * @extends Roo.bootstrap.Component
6544  * Bootstrap TableRow class
6545  * @cfg {String} cls row class
6546  * @cfg {String} align Aligns the content in a table row
6547  * @cfg {String} bgcolor Specifies a background color for a table row
6548  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6549  * @cfg {String} valign Vertical aligns the content in a table row
6550  * 
6551  * @constructor
6552  * Create a new TableRow
6553  * @param {Object} config The config object
6554  */
6555
6556 Roo.bootstrap.TableRow = function(config){
6557     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6558 };
6559
6560 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6561     
6562     cls: false,
6563     align: false,
6564     bgcolor: false,
6565     charoff: false,
6566     valign: false,
6567     
6568     getAutoCreate : function(){
6569         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6570         
6571         cfg = {
6572             tag: 'tr'
6573         };
6574             
6575         if(this.cls){
6576             cfg.cls = this.cls;
6577         }
6578         if(this.align){
6579             cfg.align = this.align;
6580         }
6581         if(this.bgcolor){
6582             cfg.bgcolor = this.bgcolor;
6583         }
6584         if(this.charoff){
6585             cfg.charoff = this.charoff;
6586         }
6587         if(this.valign){
6588             cfg.valign = this.valign;
6589         }
6590         
6591         return cfg;
6592     }
6593    
6594 });
6595
6596  
6597
6598  /*
6599  * - LGPL
6600  *
6601  * table body
6602  * 
6603  */
6604
6605 /**
6606  * @class Roo.bootstrap.TableBody
6607  * @extends Roo.bootstrap.Component
6608  * Bootstrap TableBody class
6609  * @cfg {String} cls element class
6610  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6611  * @cfg {String} align Aligns the content inside the element
6612  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6613  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6614  * 
6615  * @constructor
6616  * Create a new TableBody
6617  * @param {Object} config The config object
6618  */
6619
6620 Roo.bootstrap.TableBody = function(config){
6621     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6622 };
6623
6624 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6625     
6626     cls: false,
6627     tag: false,
6628     align: false,
6629     charoff: false,
6630     valign: false,
6631     
6632     getAutoCreate : function(){
6633         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6634         
6635         cfg = {
6636             tag: 'tbody'
6637         };
6638             
6639         if (this.cls) {
6640             cfg.cls=this.cls
6641         }
6642         if(this.tag){
6643             cfg.tag = this.tag;
6644         }
6645         
6646         if(this.align){
6647             cfg.align = this.align;
6648         }
6649         if(this.charoff){
6650             cfg.charoff = this.charoff;
6651         }
6652         if(this.valign){
6653             cfg.valign = this.valign;
6654         }
6655         
6656         return cfg;
6657     }
6658     
6659     
6660 //    initEvents : function()
6661 //    {
6662 //        
6663 //        if(!this.store){
6664 //            return;
6665 //        }
6666 //        
6667 //        this.store = Roo.factory(this.store, Roo.data);
6668 //        this.store.on('load', this.onLoad, this);
6669 //        
6670 //        this.store.load();
6671 //        
6672 //    },
6673 //    
6674 //    onLoad: function () 
6675 //    {   
6676 //        this.fireEvent('load', this);
6677 //    }
6678 //    
6679 //   
6680 });
6681
6682  
6683
6684  /*
6685  * Based on:
6686  * Ext JS Library 1.1.1
6687  * Copyright(c) 2006-2007, Ext JS, LLC.
6688  *
6689  * Originally Released Under LGPL - original licence link has changed is not relivant.
6690  *
6691  * Fork - LGPL
6692  * <script type="text/javascript">
6693  */
6694
6695 // as we use this in bootstrap.
6696 Roo.namespace('Roo.form');
6697  /**
6698  * @class Roo.form.Action
6699  * Internal Class used to handle form actions
6700  * @constructor
6701  * @param {Roo.form.BasicForm} el The form element or its id
6702  * @param {Object} config Configuration options
6703  */
6704
6705  
6706  
6707 // define the action interface
6708 Roo.form.Action = function(form, options){
6709     this.form = form;
6710     this.options = options || {};
6711 };
6712 /**
6713  * Client Validation Failed
6714  * @const 
6715  */
6716 Roo.form.Action.CLIENT_INVALID = 'client';
6717 /**
6718  * Server Validation Failed
6719  * @const 
6720  */
6721 Roo.form.Action.SERVER_INVALID = 'server';
6722  /**
6723  * Connect to Server Failed
6724  * @const 
6725  */
6726 Roo.form.Action.CONNECT_FAILURE = 'connect';
6727 /**
6728  * Reading Data from Server Failed
6729  * @const 
6730  */
6731 Roo.form.Action.LOAD_FAILURE = 'load';
6732
6733 Roo.form.Action.prototype = {
6734     type : 'default',
6735     failureType : undefined,
6736     response : undefined,
6737     result : undefined,
6738
6739     // interface method
6740     run : function(options){
6741
6742     },
6743
6744     // interface method
6745     success : function(response){
6746
6747     },
6748
6749     // interface method
6750     handleResponse : function(response){
6751
6752     },
6753
6754     // default connection failure
6755     failure : function(response){
6756         
6757         this.response = response;
6758         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6759         this.form.afterAction(this, false);
6760     },
6761
6762     processResponse : function(response){
6763         this.response = response;
6764         if(!response.responseText){
6765             return true;
6766         }
6767         this.result = this.handleResponse(response);
6768         return this.result;
6769     },
6770
6771     // utility functions used internally
6772     getUrl : function(appendParams){
6773         var url = this.options.url || this.form.url || this.form.el.dom.action;
6774         if(appendParams){
6775             var p = this.getParams();
6776             if(p){
6777                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6778             }
6779         }
6780         return url;
6781     },
6782
6783     getMethod : function(){
6784         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6785     },
6786
6787     getParams : function(){
6788         var bp = this.form.baseParams;
6789         var p = this.options.params;
6790         if(p){
6791             if(typeof p == "object"){
6792                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6793             }else if(typeof p == 'string' && bp){
6794                 p += '&' + Roo.urlEncode(bp);
6795             }
6796         }else if(bp){
6797             p = Roo.urlEncode(bp);
6798         }
6799         return p;
6800     },
6801
6802     createCallback : function(){
6803         return {
6804             success: this.success,
6805             failure: this.failure,
6806             scope: this,
6807             timeout: (this.form.timeout*1000),
6808             upload: this.form.fileUpload ? this.success : undefined
6809         };
6810     }
6811 };
6812
6813 Roo.form.Action.Submit = function(form, options){
6814     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6815 };
6816
6817 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6818     type : 'submit',
6819
6820     haveProgress : false,
6821     uploadComplete : false,
6822     
6823     // uploadProgress indicator.
6824     uploadProgress : function()
6825     {
6826         if (!this.form.progressUrl) {
6827             return;
6828         }
6829         
6830         if (!this.haveProgress) {
6831             Roo.MessageBox.progress("Uploading", "Uploading");
6832         }
6833         if (this.uploadComplete) {
6834            Roo.MessageBox.hide();
6835            return;
6836         }
6837         
6838         this.haveProgress = true;
6839    
6840         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6841         
6842         var c = new Roo.data.Connection();
6843         c.request({
6844             url : this.form.progressUrl,
6845             params: {
6846                 id : uid
6847             },
6848             method: 'GET',
6849             success : function(req){
6850                //console.log(data);
6851                 var rdata = false;
6852                 var edata;
6853                 try  {
6854                    rdata = Roo.decode(req.responseText)
6855                 } catch (e) {
6856                     Roo.log("Invalid data from server..");
6857                     Roo.log(edata);
6858                     return;
6859                 }
6860                 if (!rdata || !rdata.success) {
6861                     Roo.log(rdata);
6862                     Roo.MessageBox.alert(Roo.encode(rdata));
6863                     return;
6864                 }
6865                 var data = rdata.data;
6866                 
6867                 if (this.uploadComplete) {
6868                    Roo.MessageBox.hide();
6869                    return;
6870                 }
6871                    
6872                 if (data){
6873                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6874                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6875                     );
6876                 }
6877                 this.uploadProgress.defer(2000,this);
6878             },
6879        
6880             failure: function(data) {
6881                 Roo.log('progress url failed ');
6882                 Roo.log(data);
6883             },
6884             scope : this
6885         });
6886            
6887     },
6888     
6889     
6890     run : function()
6891     {
6892         // run get Values on the form, so it syncs any secondary forms.
6893         this.form.getValues();
6894         
6895         var o = this.options;
6896         var method = this.getMethod();
6897         var isPost = method == 'POST';
6898         if(o.clientValidation === false || this.form.isValid()){
6899             
6900             if (this.form.progressUrl) {
6901                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6902                     (new Date() * 1) + '' + Math.random());
6903                     
6904             } 
6905             
6906             
6907             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6908                 form:this.form.el.dom,
6909                 url:this.getUrl(!isPost),
6910                 method: method,
6911                 params:isPost ? this.getParams() : null,
6912                 isUpload: this.form.fileUpload
6913             }));
6914             
6915             this.uploadProgress();
6916
6917         }else if (o.clientValidation !== false){ // client validation failed
6918             this.failureType = Roo.form.Action.CLIENT_INVALID;
6919             this.form.afterAction(this, false);
6920         }
6921     },
6922
6923     success : function(response)
6924     {
6925         this.uploadComplete= true;
6926         if (this.haveProgress) {
6927             Roo.MessageBox.hide();
6928         }
6929         
6930         
6931         var result = this.processResponse(response);
6932         if(result === true || result.success){
6933             this.form.afterAction(this, true);
6934             return;
6935         }
6936         if(result.errors){
6937             this.form.markInvalid(result.errors);
6938             this.failureType = Roo.form.Action.SERVER_INVALID;
6939         }
6940         this.form.afterAction(this, false);
6941     },
6942     failure : function(response)
6943     {
6944         this.uploadComplete= true;
6945         if (this.haveProgress) {
6946             Roo.MessageBox.hide();
6947         }
6948         
6949         this.response = response;
6950         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6951         this.form.afterAction(this, false);
6952     },
6953     
6954     handleResponse : function(response){
6955         if(this.form.errorReader){
6956             var rs = this.form.errorReader.read(response);
6957             var errors = [];
6958             if(rs.records){
6959                 for(var i = 0, len = rs.records.length; i < len; i++) {
6960                     var r = rs.records[i];
6961                     errors[i] = r.data;
6962                 }
6963             }
6964             if(errors.length < 1){
6965                 errors = null;
6966             }
6967             return {
6968                 success : rs.success,
6969                 errors : errors
6970             };
6971         }
6972         var ret = false;
6973         try {
6974             ret = Roo.decode(response.responseText);
6975         } catch (e) {
6976             ret = {
6977                 success: false,
6978                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6979                 errors : []
6980             };
6981         }
6982         return ret;
6983         
6984     }
6985 });
6986
6987
6988 Roo.form.Action.Load = function(form, options){
6989     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6990     this.reader = this.form.reader;
6991 };
6992
6993 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6994     type : 'load',
6995
6996     run : function(){
6997         
6998         Roo.Ajax.request(Roo.apply(
6999                 this.createCallback(), {
7000                     method:this.getMethod(),
7001                     url:this.getUrl(false),
7002                     params:this.getParams()
7003         }));
7004     },
7005
7006     success : function(response){
7007         
7008         var result = this.processResponse(response);
7009         if(result === true || !result.success || !result.data){
7010             this.failureType = Roo.form.Action.LOAD_FAILURE;
7011             this.form.afterAction(this, false);
7012             return;
7013         }
7014         this.form.clearInvalid();
7015         this.form.setValues(result.data);
7016         this.form.afterAction(this, true);
7017     },
7018
7019     handleResponse : function(response){
7020         if(this.form.reader){
7021             var rs = this.form.reader.read(response);
7022             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7023             return {
7024                 success : rs.success,
7025                 data : data
7026             };
7027         }
7028         return Roo.decode(response.responseText);
7029     }
7030 });
7031
7032 Roo.form.Action.ACTION_TYPES = {
7033     'load' : Roo.form.Action.Load,
7034     'submit' : Roo.form.Action.Submit
7035 };/*
7036  * - LGPL
7037  *
7038  * form
7039  * 
7040  */
7041
7042 /**
7043  * @class Roo.bootstrap.Form
7044  * @extends Roo.bootstrap.Component
7045  * Bootstrap Form class
7046  * @cfg {String} method  GET | POST (default POST)
7047  * @cfg {String} labelAlign top | left (default top)
7048  * @cfg {String} align left  | right - for navbars
7049  * @cfg {Boolean} loadMask load mask when submit (default true)
7050
7051  * 
7052  * @constructor
7053  * Create a new Form
7054  * @param {Object} config The config object
7055  */
7056
7057
7058 Roo.bootstrap.Form = function(config){
7059     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7060     this.addEvents({
7061         /**
7062          * @event clientvalidation
7063          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7064          * @param {Form} this
7065          * @param {Boolean} valid true if the form has passed client-side validation
7066          */
7067         clientvalidation: true,
7068         /**
7069          * @event beforeaction
7070          * Fires before any action is performed. Return false to cancel the action.
7071          * @param {Form} this
7072          * @param {Action} action The action to be performed
7073          */
7074         beforeaction: true,
7075         /**
7076          * @event actionfailed
7077          * Fires when an action fails.
7078          * @param {Form} this
7079          * @param {Action} action The action that failed
7080          */
7081         actionfailed : true,
7082         /**
7083          * @event actioncomplete
7084          * Fires when an action is completed.
7085          * @param {Form} this
7086          * @param {Action} action The action that completed
7087          */
7088         actioncomplete : true
7089     });
7090     
7091 };
7092
7093 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7094       
7095      /**
7096      * @cfg {String} method
7097      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7098      */
7099     method : 'POST',
7100     /**
7101      * @cfg {String} url
7102      * The URL to use for form actions if one isn't supplied in the action options.
7103      */
7104     /**
7105      * @cfg {Boolean} fileUpload
7106      * Set to true if this form is a file upload.
7107      */
7108      
7109     /**
7110      * @cfg {Object} baseParams
7111      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7112      */
7113       
7114     /**
7115      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7116      */
7117     timeout: 30,
7118     /**
7119      * @cfg {Sting} align (left|right) for navbar forms
7120      */
7121     align : 'left',
7122
7123     // private
7124     activeAction : null,
7125  
7126     /**
7127      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7128      * element by passing it or its id or mask the form itself by passing in true.
7129      * @type Mixed
7130      */
7131     waitMsgTarget : false,
7132     
7133     loadMask : true,
7134     
7135     getAutoCreate : function(){
7136         
7137         var cfg = {
7138             tag: 'form',
7139             method : this.method || 'POST',
7140             id : this.id || Roo.id(),
7141             cls : ''
7142         };
7143         if (this.parent().xtype.match(/^Nav/)) {
7144             cfg.cls = 'navbar-form navbar-' + this.align;
7145             
7146         }
7147         
7148         if (this.labelAlign == 'left' ) {
7149             cfg.cls += ' form-horizontal';
7150         }
7151         
7152         
7153         return cfg;
7154     },
7155     initEvents : function()
7156     {
7157         this.el.on('submit', this.onSubmit, this);
7158         // this was added as random key presses on the form where triggering form submit.
7159         this.el.on('keypress', function(e) {
7160             if (e.getCharCode() != 13) {
7161                 return true;
7162             }
7163             // we might need to allow it for textareas.. and some other items.
7164             // check e.getTarget().
7165             
7166             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7167                 return true;
7168             }
7169         
7170             Roo.log("keypress blocked");
7171             
7172             e.preventDefault();
7173             return false;
7174         });
7175         
7176     },
7177     // private
7178     onSubmit : function(e){
7179         e.stopEvent();
7180     },
7181     
7182      /**
7183      * Returns true if client-side validation on the form is successful.
7184      * @return Boolean
7185      */
7186     isValid : function(){
7187         var items = this.getItems();
7188         var valid = true;
7189         items.each(function(f){
7190            if(!f.validate()){
7191                valid = false;
7192                
7193            }
7194         });
7195         return valid;
7196     },
7197     /**
7198      * Returns true if any fields in this form have changed since their original load.
7199      * @return Boolean
7200      */
7201     isDirty : function(){
7202         var dirty = false;
7203         var items = this.getItems();
7204         items.each(function(f){
7205            if(f.isDirty()){
7206                dirty = true;
7207                return false;
7208            }
7209            return true;
7210         });
7211         return dirty;
7212     },
7213      /**
7214      * Performs a predefined action (submit or load) or custom actions you define on this form.
7215      * @param {String} actionName The name of the action type
7216      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7217      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7218      * accept other config options):
7219      * <pre>
7220 Property          Type             Description
7221 ----------------  ---------------  ----------------------------------------------------------------------------------
7222 url               String           The url for the action (defaults to the form's url)
7223 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7224 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7225 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7226                                    validate the form on the client (defaults to false)
7227      * </pre>
7228      * @return {BasicForm} this
7229      */
7230     doAction : function(action, options){
7231         if(typeof action == 'string'){
7232             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7233         }
7234         if(this.fireEvent('beforeaction', this, action) !== false){
7235             this.beforeAction(action);
7236             action.run.defer(100, action);
7237         }
7238         return this;
7239     },
7240     
7241     // private
7242     beforeAction : function(action){
7243         var o = action.options;
7244         
7245         if(this.loadMask){
7246             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7247         }
7248         // not really supported yet.. ??
7249         
7250         //if(this.waitMsgTarget === true){
7251         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7252         //}else if(this.waitMsgTarget){
7253         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7254         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7255         //}else {
7256         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7257        // }
7258          
7259     },
7260
7261     // private
7262     afterAction : function(action, success){
7263         this.activeAction = null;
7264         var o = action.options;
7265         
7266         //if(this.waitMsgTarget === true){
7267             this.el.unmask();
7268         //}else if(this.waitMsgTarget){
7269         //    this.waitMsgTarget.unmask();
7270         //}else{
7271         //    Roo.MessageBox.updateProgress(1);
7272         //    Roo.MessageBox.hide();
7273        // }
7274         // 
7275         if(success){
7276             if(o.reset){
7277                 this.reset();
7278             }
7279             Roo.callback(o.success, o.scope, [this, action]);
7280             this.fireEvent('actioncomplete', this, action);
7281             
7282         }else{
7283             
7284             // failure condition..
7285             // we have a scenario where updates need confirming.
7286             // eg. if a locking scenario exists..
7287             // we look for { errors : { needs_confirm : true }} in the response.
7288             if (
7289                 (typeof(action.result) != 'undefined')  &&
7290                 (typeof(action.result.errors) != 'undefined')  &&
7291                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7292            ){
7293                 var _t = this;
7294                 Roo.log("not supported yet");
7295                  /*
7296                 
7297                 Roo.MessageBox.confirm(
7298                     "Change requires confirmation",
7299                     action.result.errorMsg,
7300                     function(r) {
7301                         if (r != 'yes') {
7302                             return;
7303                         }
7304                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7305                     }
7306                     
7307                 );
7308                 */
7309                 
7310                 
7311                 return;
7312             }
7313             
7314             Roo.callback(o.failure, o.scope, [this, action]);
7315             // show an error message if no failed handler is set..
7316             if (!this.hasListener('actionfailed')) {
7317                 Roo.log("need to add dialog support");
7318                 /*
7319                 Roo.MessageBox.alert("Error",
7320                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7321                         action.result.errorMsg :
7322                         "Saving Failed, please check your entries or try again"
7323                 );
7324                 */
7325             }
7326             
7327             this.fireEvent('actionfailed', this, action);
7328         }
7329         
7330     },
7331     /**
7332      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7333      * @param {String} id The value to search for
7334      * @return Field
7335      */
7336     findField : function(id){
7337         var items = this.getItems();
7338         var field = items.get(id);
7339         if(!field){
7340              items.each(function(f){
7341                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7342                     field = f;
7343                     return false;
7344                 }
7345                 return true;
7346             });
7347         }
7348         return field || null;
7349     },
7350      /**
7351      * Mark fields in this form invalid in bulk.
7352      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7353      * @return {BasicForm} this
7354      */
7355     markInvalid : function(errors){
7356         if(errors instanceof Array){
7357             for(var i = 0, len = errors.length; i < len; i++){
7358                 var fieldError = errors[i];
7359                 var f = this.findField(fieldError.id);
7360                 if(f){
7361                     f.markInvalid(fieldError.msg);
7362                 }
7363             }
7364         }else{
7365             var field, id;
7366             for(id in errors){
7367                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7368                     field.markInvalid(errors[id]);
7369                 }
7370             }
7371         }
7372         //Roo.each(this.childForms || [], function (f) {
7373         //    f.markInvalid(errors);
7374         //});
7375         
7376         return this;
7377     },
7378
7379     /**
7380      * Set values for fields in this form in bulk.
7381      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7382      * @return {BasicForm} this
7383      */
7384     setValues : function(values){
7385         if(values instanceof Array){ // array of objects
7386             for(var i = 0, len = values.length; i < len; i++){
7387                 var v = values[i];
7388                 var f = this.findField(v.id);
7389                 if(f){
7390                     f.setValue(v.value);
7391                     if(this.trackResetOnLoad){
7392                         f.originalValue = f.getValue();
7393                     }
7394                 }
7395             }
7396         }else{ // object hash
7397             var field, id;
7398             for(id in values){
7399                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7400                     
7401                     if (field.setFromData && 
7402                         field.valueField && 
7403                         field.displayField &&
7404                         // combos' with local stores can 
7405                         // be queried via setValue()
7406                         // to set their value..
7407                         (field.store && !field.store.isLocal)
7408                         ) {
7409                         // it's a combo
7410                         var sd = { };
7411                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7412                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7413                         field.setFromData(sd);
7414                         
7415                     } else {
7416                         field.setValue(values[id]);
7417                     }
7418                     
7419                     
7420                     if(this.trackResetOnLoad){
7421                         field.originalValue = field.getValue();
7422                     }
7423                 }
7424             }
7425         }
7426          
7427         //Roo.each(this.childForms || [], function (f) {
7428         //    f.setValues(values);
7429         //});
7430                 
7431         return this;
7432     },
7433
7434     /**
7435      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7436      * they are returned as an array.
7437      * @param {Boolean} asString
7438      * @return {Object}
7439      */
7440     getValues : function(asString){
7441         //if (this.childForms) {
7442             // copy values from the child forms
7443         //    Roo.each(this.childForms, function (f) {
7444         //        this.setValues(f.getValues());
7445         //    }, this);
7446         //}
7447         
7448         
7449         
7450         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7451         if(asString === true){
7452             return fs;
7453         }
7454         return Roo.urlDecode(fs);
7455     },
7456     
7457     /**
7458      * Returns the fields in this form as an object with key/value pairs. 
7459      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7460      * @return {Object}
7461      */
7462     getFieldValues : function(with_hidden)
7463     {
7464         var items = this.getItems();
7465         var ret = {};
7466         items.each(function(f){
7467             if (!f.getName()) {
7468                 return;
7469             }
7470             var v = f.getValue();
7471             if (f.inputType =='radio') {
7472                 if (typeof(ret[f.getName()]) == 'undefined') {
7473                     ret[f.getName()] = ''; // empty..
7474                 }
7475                 
7476                 if (!f.el.dom.checked) {
7477                     return;
7478                     
7479                 }
7480                 v = f.el.dom.value;
7481                 
7482             }
7483             
7484             // not sure if this supported any more..
7485             if ((typeof(v) == 'object') && f.getRawValue) {
7486                 v = f.getRawValue() ; // dates..
7487             }
7488             // combo boxes where name != hiddenName...
7489             if (f.name != f.getName()) {
7490                 ret[f.name] = f.getRawValue();
7491             }
7492             ret[f.getName()] = v;
7493         });
7494         
7495         return ret;
7496     },
7497
7498     /**
7499      * Clears all invalid messages in this form.
7500      * @return {BasicForm} this
7501      */
7502     clearInvalid : function(){
7503         var items = this.getItems();
7504         
7505         items.each(function(f){
7506            f.clearInvalid();
7507         });
7508         
7509         
7510         
7511         return this;
7512     },
7513
7514     /**
7515      * Resets this form.
7516      * @return {BasicForm} this
7517      */
7518     reset : function(){
7519         var items = this.getItems();
7520         items.each(function(f){
7521             f.reset();
7522         });
7523         
7524         Roo.each(this.childForms || [], function (f) {
7525             f.reset();
7526         });
7527        
7528         
7529         return this;
7530     },
7531     getItems : function()
7532     {
7533         var r=new Roo.util.MixedCollection(false, function(o){
7534             return o.id || (o.id = Roo.id());
7535         });
7536         var iter = function(el) {
7537             if (el.inputEl) {
7538                 r.add(el);
7539             }
7540             if (!el.items) {
7541                 return;
7542             }
7543             Roo.each(el.items,function(e) {
7544                 iter(e);
7545             });
7546             
7547             
7548         };
7549         
7550         iter(this);
7551         return r;
7552         
7553         
7554         
7555         
7556     }
7557     
7558 });
7559
7560  
7561 /*
7562  * Based on:
7563  * Ext JS Library 1.1.1
7564  * Copyright(c) 2006-2007, Ext JS, LLC.
7565  *
7566  * Originally Released Under LGPL - original licence link has changed is not relivant.
7567  *
7568  * Fork - LGPL
7569  * <script type="text/javascript">
7570  */
7571 /**
7572  * @class Roo.form.VTypes
7573  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7574  * @singleton
7575  */
7576 Roo.form.VTypes = function(){
7577     // closure these in so they are only created once.
7578     var alpha = /^[a-zA-Z_]+$/;
7579     var alphanum = /^[a-zA-Z0-9_]+$/;
7580     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7581     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7582
7583     // All these messages and functions are configurable
7584     return {
7585         /**
7586          * The function used to validate email addresses
7587          * @param {String} value The email address
7588          */
7589         'email' : function(v){
7590             return email.test(v);
7591         },
7592         /**
7593          * The error text to display when the email validation function returns false
7594          * @type String
7595          */
7596         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7597         /**
7598          * The keystroke filter mask to be applied on email input
7599          * @type RegExp
7600          */
7601         'emailMask' : /[a-z0-9_\.\-@]/i,
7602
7603         /**
7604          * The function used to validate URLs
7605          * @param {String} value The URL
7606          */
7607         'url' : function(v){
7608             return url.test(v);
7609         },
7610         /**
7611          * The error text to display when the url validation function returns false
7612          * @type String
7613          */
7614         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7615         
7616         /**
7617          * The function used to validate alpha values
7618          * @param {String} value The value
7619          */
7620         'alpha' : function(v){
7621             return alpha.test(v);
7622         },
7623         /**
7624          * The error text to display when the alpha validation function returns false
7625          * @type String
7626          */
7627         'alphaText' : 'This field should only contain letters and _',
7628         /**
7629          * The keystroke filter mask to be applied on alpha input
7630          * @type RegExp
7631          */
7632         'alphaMask' : /[a-z_]/i,
7633
7634         /**
7635          * The function used to validate alphanumeric values
7636          * @param {String} value The value
7637          */
7638         'alphanum' : function(v){
7639             return alphanum.test(v);
7640         },
7641         /**
7642          * The error text to display when the alphanumeric validation function returns false
7643          * @type String
7644          */
7645         'alphanumText' : 'This field should only contain letters, numbers and _',
7646         /**
7647          * The keystroke filter mask to be applied on alphanumeric input
7648          * @type RegExp
7649          */
7650         'alphanumMask' : /[a-z0-9_]/i
7651     };
7652 }();/*
7653  * - LGPL
7654  *
7655  * Input
7656  * 
7657  */
7658
7659 /**
7660  * @class Roo.bootstrap.Input
7661  * @extends Roo.bootstrap.Component
7662  * Bootstrap Input class
7663  * @cfg {Boolean} disabled is it disabled
7664  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7665  * @cfg {String} name name of the input
7666  * @cfg {string} fieldLabel - the label associated
7667  * @cfg {string} placeholder - placeholder to put in text.
7668  * @cfg {string}  before - input group add on before
7669  * @cfg {string} after - input group add on after
7670  * @cfg {string} size - (lg|sm) or leave empty..
7671  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7672  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7673  * @cfg {Number} md colspan out of 12 for computer-sized screens
7674  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7675  * @cfg {string} value default value of the input
7676  * @cfg {Number} labelWidth set the width of label (0-12)
7677  * @cfg {String} labelAlign (top|left)
7678  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7679  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7680
7681  * @cfg {String} align (left|center|right) Default left
7682  * @cfg {Boolean} forceFeedback (true|false) Default false
7683  * 
7684  * 
7685  * 
7686  * 
7687  * @constructor
7688  * Create a new Input
7689  * @param {Object} config The config object
7690  */
7691
7692 Roo.bootstrap.Input = function(config){
7693     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7694    
7695         this.addEvents({
7696             /**
7697              * @event focus
7698              * Fires when this field receives input focus.
7699              * @param {Roo.form.Field} this
7700              */
7701             focus : true,
7702             /**
7703              * @event blur
7704              * Fires when this field loses input focus.
7705              * @param {Roo.form.Field} this
7706              */
7707             blur : true,
7708             /**
7709              * @event specialkey
7710              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7711              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7712              * @param {Roo.form.Field} this
7713              * @param {Roo.EventObject} e The event object
7714              */
7715             specialkey : true,
7716             /**
7717              * @event change
7718              * Fires just before the field blurs if the field value has changed.
7719              * @param {Roo.form.Field} this
7720              * @param {Mixed} newValue The new value
7721              * @param {Mixed} oldValue The original value
7722              */
7723             change : true,
7724             /**
7725              * @event invalid
7726              * Fires after the field has been marked as invalid.
7727              * @param {Roo.form.Field} this
7728              * @param {String} msg The validation message
7729              */
7730             invalid : true,
7731             /**
7732              * @event valid
7733              * Fires after the field has been validated with no errors.
7734              * @param {Roo.form.Field} this
7735              */
7736             valid : true,
7737              /**
7738              * @event keyup
7739              * Fires after the key up
7740              * @param {Roo.form.Field} this
7741              * @param {Roo.EventObject}  e The event Object
7742              */
7743             keyup : true
7744         });
7745 };
7746
7747 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7748      /**
7749      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7750       automatic validation (defaults to "keyup").
7751      */
7752     validationEvent : "keyup",
7753      /**
7754      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7755      */
7756     validateOnBlur : true,
7757     /**
7758      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7759      */
7760     validationDelay : 250,
7761      /**
7762      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7763      */
7764     focusClass : "x-form-focus",  // not needed???
7765     
7766        
7767     /**
7768      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7769      */
7770     invalidClass : "has-warning",
7771     
7772     /**
7773      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7774      */
7775     validClass : "has-success",
7776     
7777     /**
7778      * @cfg {Boolean} hasFeedback (true|false) default true
7779      */
7780     hasFeedback : true,
7781     
7782     /**
7783      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7784      */
7785     invalidFeedbackClass : "glyphicon-warning-sign",
7786     
7787     /**
7788      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7789      */
7790     validFeedbackClass : "glyphicon-ok",
7791     
7792     /**
7793      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7794      */
7795     selectOnFocus : false,
7796     
7797      /**
7798      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7799      */
7800     maskRe : null,
7801        /**
7802      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7803      */
7804     vtype : null,
7805     
7806       /**
7807      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7808      */
7809     disableKeyFilter : false,
7810     
7811        /**
7812      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7813      */
7814     disabled : false,
7815      /**
7816      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7817      */
7818     allowBlank : true,
7819     /**
7820      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7821      */
7822     blankText : "This field is required",
7823     
7824      /**
7825      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7826      */
7827     minLength : 0,
7828     /**
7829      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7830      */
7831     maxLength : Number.MAX_VALUE,
7832     /**
7833      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7834      */
7835     minLengthText : "The minimum length for this field is {0}",
7836     /**
7837      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7838      */
7839     maxLengthText : "The maximum length for this field is {0}",
7840   
7841     
7842     /**
7843      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7844      * If available, this function will be called only after the basic validators all return true, and will be passed the
7845      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7846      */
7847     validator : null,
7848     /**
7849      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7850      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7851      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7852      */
7853     regex : null,
7854     /**
7855      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7856      */
7857     regexText : "",
7858     
7859     autocomplete: false,
7860     
7861     
7862     fieldLabel : '',
7863     inputType : 'text',
7864     
7865     name : false,
7866     placeholder: false,
7867     before : false,
7868     after : false,
7869     size : false,
7870     hasFocus : false,
7871     preventMark: false,
7872     isFormField : true,
7873     value : '',
7874     labelWidth : 2,
7875     labelAlign : false,
7876     readOnly : false,
7877     align : false,
7878     formatedValue : false,
7879     forceFeedback : false,
7880     
7881     parentLabelAlign : function()
7882     {
7883         var parent = this;
7884         while (parent.parent()) {
7885             parent = parent.parent();
7886             if (typeof(parent.labelAlign) !='undefined') {
7887                 return parent.labelAlign;
7888             }
7889         }
7890         return 'left';
7891         
7892     },
7893     
7894     getAutoCreate : function(){
7895         
7896         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7897         
7898         var id = Roo.id();
7899         
7900         var cfg = {};
7901         
7902         if(this.inputType != 'hidden'){
7903             cfg.cls = 'form-group' //input-group
7904         }
7905         
7906         var input =  {
7907             tag: 'input',
7908             id : id,
7909             type : this.inputType,
7910             value : this.value,
7911             cls : 'form-control',
7912             placeholder : this.placeholder || '',
7913             autocomplete : this.autocomplete || 'new-password'
7914         };
7915         
7916         
7917         if(this.align){
7918             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7919         }
7920         
7921         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7922             input.maxLength = this.maxLength;
7923         }
7924         
7925         if (this.disabled) {
7926             input.disabled=true;
7927         }
7928         
7929         if (this.readOnly) {
7930             input.readonly=true;
7931         }
7932         
7933         if (this.name) {
7934             input.name = this.name;
7935         }
7936         if (this.size) {
7937             input.cls += ' input-' + this.size;
7938         }
7939         var settings=this;
7940         ['xs','sm','md','lg'].map(function(size){
7941             if (settings[size]) {
7942                 cfg.cls += ' col-' + size + '-' + settings[size];
7943             }
7944         });
7945         
7946         var inputblock = input;
7947         
7948         var feedback = {
7949             tag: 'span',
7950             cls: 'glyphicon form-control-feedback'
7951         };
7952             
7953         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7954             
7955             inputblock = {
7956                 cls : 'has-feedback',
7957                 cn :  [
7958                     input,
7959                     feedback
7960                 ] 
7961             };  
7962         }
7963         
7964         if (this.before || this.after) {
7965             
7966             inputblock = {
7967                 cls : 'input-group',
7968                 cn :  [] 
7969             };
7970             
7971             if (this.before && typeof(this.before) == 'string') {
7972                 
7973                 inputblock.cn.push({
7974                     tag :'span',
7975                     cls : 'roo-input-before input-group-addon',
7976                     html : this.before
7977                 });
7978             }
7979             if (this.before && typeof(this.before) == 'object') {
7980                 this.before = Roo.factory(this.before);
7981                 Roo.log(this.before);
7982                 inputblock.cn.push({
7983                     tag :'span',
7984                     cls : 'roo-input-before input-group-' +
7985                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7986                 });
7987             }
7988             
7989             inputblock.cn.push(input);
7990             
7991             if (this.after && typeof(this.after) == 'string') {
7992                 inputblock.cn.push({
7993                     tag :'span',
7994                     cls : 'roo-input-after input-group-addon',
7995                     html : this.after
7996                 });
7997             }
7998             if (this.after && typeof(this.after) == 'object') {
7999                 this.after = Roo.factory(this.after);
8000                 Roo.log(this.after);
8001                 inputblock.cn.push({
8002                     tag :'span',
8003                     cls : 'roo-input-after input-group-' +
8004                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8005                 });
8006             }
8007             
8008             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8009                 inputblock.cls += ' has-feedback';
8010                 inputblock.cn.push(feedback);
8011             }
8012         };
8013         
8014         if (align ==='left' && this.fieldLabel.length) {
8015                 Roo.log("left and has label");
8016                 cfg.cn = [
8017                     
8018                     {
8019                         tag: 'label',
8020                         'for' :  id,
8021                         cls : 'control-label col-sm-' + this.labelWidth,
8022                         html : this.fieldLabel
8023                         
8024                     },
8025                     {
8026                         cls : "col-sm-" + (12 - this.labelWidth), 
8027                         cn: [
8028                             inputblock
8029                         ]
8030                     }
8031                     
8032                 ];
8033         } else if ( this.fieldLabel.length) {
8034                 Roo.log(" label");
8035                  cfg.cn = [
8036                    
8037                     {
8038                         tag: 'label',
8039                         //cls : 'input-group-addon',
8040                         html : this.fieldLabel
8041                         
8042                     },
8043                     
8044                     inputblock
8045                     
8046                 ];
8047
8048         } else {
8049             
8050                 Roo.log(" no label && no align");
8051                 cfg.cn = [
8052                     
8053                         inputblock
8054                     
8055                 ];
8056                 
8057                 
8058         };
8059         Roo.log('input-parentType: ' + this.parentType);
8060         
8061         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8062            cfg.cls += ' navbar-form';
8063            Roo.log(cfg);
8064         }
8065         
8066         return cfg;
8067         
8068     },
8069     /**
8070      * return the real input element.
8071      */
8072     inputEl: function ()
8073     {
8074         return this.el.select('input.form-control',true).first();
8075     },
8076     
8077     tooltipEl : function()
8078     {
8079         return this.inputEl();
8080     },
8081     
8082     setDisabled : function(v)
8083     {
8084         var i  = this.inputEl().dom;
8085         if (!v) {
8086             i.removeAttribute('disabled');
8087             return;
8088             
8089         }
8090         i.setAttribute('disabled','true');
8091     },
8092     initEvents : function()
8093     {
8094           
8095         this.inputEl().on("keydown" , this.fireKey,  this);
8096         this.inputEl().on("focus", this.onFocus,  this);
8097         this.inputEl().on("blur", this.onBlur,  this);
8098         
8099         this.inputEl().relayEvent('keyup', this);
8100  
8101         // reference to original value for reset
8102         this.originalValue = this.getValue();
8103         //Roo.form.TextField.superclass.initEvents.call(this);
8104         if(this.validationEvent == 'keyup'){
8105             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8106             this.inputEl().on('keyup', this.filterValidation, this);
8107         }
8108         else if(this.validationEvent !== false){
8109             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8110         }
8111         
8112         if(this.selectOnFocus){
8113             this.on("focus", this.preFocus, this);
8114             
8115         }
8116         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8117             this.inputEl().on("keypress", this.filterKeys, this);
8118         }
8119        /* if(this.grow){
8120             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8121             this.el.on("click", this.autoSize,  this);
8122         }
8123         */
8124         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8125             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8126         }
8127         
8128         if (typeof(this.before) == 'object') {
8129             this.before.render(this.el.select('.roo-input-before',true).first());
8130         }
8131         if (typeof(this.after) == 'object') {
8132             this.after.render(this.el.select('.roo-input-after',true).first());
8133         }
8134         
8135         
8136     },
8137     filterValidation : function(e){
8138         if(!e.isNavKeyPress()){
8139             this.validationTask.delay(this.validationDelay);
8140         }
8141     },
8142      /**
8143      * Validates the field value
8144      * @return {Boolean} True if the value is valid, else false
8145      */
8146     validate : function(){
8147         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8148         if(this.disabled || this.validateValue(this.getRawValue())){
8149             this.markValid();
8150             return true;
8151         }
8152         
8153         this.markInvalid();
8154         return false;
8155     },
8156     
8157     
8158     /**
8159      * Validates a value according to the field's validation rules and marks the field as invalid
8160      * if the validation fails
8161      * @param {Mixed} value The value to validate
8162      * @return {Boolean} True if the value is valid, else false
8163      */
8164     validateValue : function(value){
8165         if(value.length < 1)  { // if it's blank
8166             if(this.allowBlank){
8167                 return true;
8168             }
8169             return false;
8170         }
8171         
8172         if(value.length < this.minLength){
8173             return false;
8174         }
8175         if(value.length > this.maxLength){
8176             return false;
8177         }
8178         if(this.vtype){
8179             var vt = Roo.form.VTypes;
8180             if(!vt[this.vtype](value, this)){
8181                 return false;
8182             }
8183         }
8184         if(typeof this.validator == "function"){
8185             var msg = this.validator(value);
8186             if(msg !== true){
8187                 return false;
8188             }
8189         }
8190         
8191         if(this.regex && !this.regex.test(value)){
8192             return false;
8193         }
8194         
8195         return true;
8196     },
8197
8198     
8199     
8200      // private
8201     fireKey : function(e){
8202         //Roo.log('field ' + e.getKey());
8203         if(e.isNavKeyPress()){
8204             this.fireEvent("specialkey", this, e);
8205         }
8206     },
8207     focus : function (selectText){
8208         if(this.rendered){
8209             this.inputEl().focus();
8210             if(selectText === true){
8211                 this.inputEl().dom.select();
8212             }
8213         }
8214         return this;
8215     } ,
8216     
8217     onFocus : function(){
8218         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8219            // this.el.addClass(this.focusClass);
8220         }
8221         if(!this.hasFocus){
8222             this.hasFocus = true;
8223             this.startValue = this.getValue();
8224             this.fireEvent("focus", this);
8225         }
8226     },
8227     
8228     beforeBlur : Roo.emptyFn,
8229
8230     
8231     // private
8232     onBlur : function(){
8233         this.beforeBlur();
8234         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8235             //this.el.removeClass(this.focusClass);
8236         }
8237         this.hasFocus = false;
8238         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8239             this.validate();
8240         }
8241         var v = this.getValue();
8242         if(String(v) !== String(this.startValue)){
8243             this.fireEvent('change', this, v, this.startValue);
8244         }
8245         this.fireEvent("blur", this);
8246     },
8247     
8248     /**
8249      * Resets the current field value to the originally loaded value and clears any validation messages
8250      */
8251     reset : function(){
8252         this.setValue(this.originalValue);
8253         this.validate();
8254     },
8255      /**
8256      * Returns the name of the field
8257      * @return {Mixed} name The name field
8258      */
8259     getName: function(){
8260         return this.name;
8261     },
8262      /**
8263      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8264      * @return {Mixed} value The field value
8265      */
8266     getValue : function(){
8267         
8268         var v = this.inputEl().getValue();
8269         
8270         return v;
8271     },
8272     /**
8273      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8274      * @return {Mixed} value The field value
8275      */
8276     getRawValue : function(){
8277         var v = this.inputEl().getValue();
8278         
8279         return v;
8280     },
8281     
8282     /**
8283      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8284      * @param {Mixed} value The value to set
8285      */
8286     setRawValue : function(v){
8287         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8288     },
8289     
8290     selectText : function(start, end){
8291         var v = this.getRawValue();
8292         if(v.length > 0){
8293             start = start === undefined ? 0 : start;
8294             end = end === undefined ? v.length : end;
8295             var d = this.inputEl().dom;
8296             if(d.setSelectionRange){
8297                 d.setSelectionRange(start, end);
8298             }else if(d.createTextRange){
8299                 var range = d.createTextRange();
8300                 range.moveStart("character", start);
8301                 range.moveEnd("character", v.length-end);
8302                 range.select();
8303             }
8304         }
8305     },
8306     
8307     /**
8308      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8309      * @param {Mixed} value The value to set
8310      */
8311     setValue : function(v){
8312         this.value = v;
8313         if(this.rendered){
8314             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8315             this.validate();
8316         }
8317     },
8318     
8319     /*
8320     processValue : function(value){
8321         if(this.stripCharsRe){
8322             var newValue = value.replace(this.stripCharsRe, '');
8323             if(newValue !== value){
8324                 this.setRawValue(newValue);
8325                 return newValue;
8326             }
8327         }
8328         return value;
8329     },
8330   */
8331     preFocus : function(){
8332         
8333         if(this.selectOnFocus){
8334             this.inputEl().dom.select();
8335         }
8336     },
8337     filterKeys : function(e){
8338         var k = e.getKey();
8339         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8340             return;
8341         }
8342         var c = e.getCharCode(), cc = String.fromCharCode(c);
8343         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8344             return;
8345         }
8346         if(!this.maskRe.test(cc)){
8347             e.stopEvent();
8348         }
8349     },
8350      /**
8351      * Clear any invalid styles/messages for this field
8352      */
8353     clearInvalid : function(){
8354         
8355         if(!this.el || this.preventMark){ // not rendered
8356             return;
8357         }
8358         this.el.removeClass(this.invalidClass);
8359         
8360         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8361             
8362             var feedback = this.el.select('.form-control-feedback', true).first();
8363             
8364             if(feedback){
8365                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8366             }
8367             
8368         }
8369         
8370         this.fireEvent('valid', this);
8371     },
8372     
8373      /**
8374      * Mark this field as valid
8375      */
8376     markValid : function()
8377     {
8378         if(!this.el  || this.preventMark){ // not rendered
8379             return;
8380         }
8381         
8382         this.el.removeClass([this.invalidClass, this.validClass]);
8383         
8384         var feedback = this.el.select('.form-control-feedback', true).first();
8385             
8386         if(feedback){
8387             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8388         }
8389
8390         if(this.disabled || this.allowBlank){
8391             return;
8392         }
8393         
8394         var formGroup = this.el.findParent('.form-group', false, true);
8395         
8396         if(formGroup){
8397             
8398             var label = formGroup.select('label', true).first();
8399             var icon = formGroup.select('i.fa-star', true).first();
8400             
8401             if(label && icon){
8402                 icon.remove();
8403             }
8404         }
8405         
8406         this.el.addClass(this.validClass);
8407         
8408         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8409             
8410             var feedback = this.el.select('.form-control-feedback', true).first();
8411             
8412             if(feedback){
8413                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8414                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8415             }
8416             
8417         }
8418         
8419         this.fireEvent('valid', this);
8420     },
8421     
8422      /**
8423      * Mark this field as invalid
8424      * @param {String} msg The validation message
8425      */
8426     markInvalid : function(msg)
8427     {
8428         if(!this.el  || this.preventMark){ // not rendered
8429             return;
8430         }
8431         
8432         this.el.removeClass([this.invalidClass, this.validClass]);
8433         
8434         var feedback = this.el.select('.form-control-feedback', true).first();
8435             
8436         if(feedback){
8437             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8438         }
8439
8440         if(this.disabled || this.allowBlank){
8441             return;
8442         }
8443         
8444         var formGroup = this.el.findParent('.form-group', false, true);
8445         
8446         if(formGroup){
8447             var label = formGroup.select('label', true).first();
8448             var icon = formGroup.select('i.fa-star', true).first();
8449
8450             if(!this.getValue().length && label && !icon){
8451                 this.el.findParent('.form-group', false, true).createChild({
8452                     tag : 'i',
8453                     cls : 'text-danger fa fa-lg fa-star',
8454                     tooltip : 'This field is required',
8455                     style : 'margin-right:5px;'
8456                 }, label, true);
8457             }
8458         }
8459         
8460         
8461         this.el.addClass(this.invalidClass);
8462         
8463         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8464             
8465             var feedback = this.el.select('.form-control-feedback', true).first();
8466             
8467             if(feedback){
8468                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8469                 
8470                 if(this.getValue().length || this.forceFeedback){
8471                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8472                 }
8473                 
8474             }
8475             
8476         }
8477         
8478         this.fireEvent('invalid', this, msg);
8479     },
8480     // private
8481     SafariOnKeyDown : function(event)
8482     {
8483         // this is a workaround for a password hang bug on chrome/ webkit.
8484         
8485         var isSelectAll = false;
8486         
8487         if(this.inputEl().dom.selectionEnd > 0){
8488             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8489         }
8490         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8491             event.preventDefault();
8492             this.setValue('');
8493             return;
8494         }
8495         
8496         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8497             
8498             event.preventDefault();
8499             // this is very hacky as keydown always get's upper case.
8500             //
8501             var cc = String.fromCharCode(event.getCharCode());
8502             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8503             
8504         }
8505     },
8506     adjustWidth : function(tag, w){
8507         tag = tag.toLowerCase();
8508         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8509             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8510                 if(tag == 'input'){
8511                     return w + 2;
8512                 }
8513                 if(tag == 'textarea'){
8514                     return w-2;
8515                 }
8516             }else if(Roo.isOpera){
8517                 if(tag == 'input'){
8518                     return w + 2;
8519                 }
8520                 if(tag == 'textarea'){
8521                     return w-2;
8522                 }
8523             }
8524         }
8525         return w;
8526     }
8527     
8528 });
8529
8530  
8531 /*
8532  * - LGPL
8533  *
8534  * Input
8535  * 
8536  */
8537
8538 /**
8539  * @class Roo.bootstrap.TextArea
8540  * @extends Roo.bootstrap.Input
8541  * Bootstrap TextArea class
8542  * @cfg {Number} cols Specifies the visible width of a text area
8543  * @cfg {Number} rows Specifies the visible number of lines in a text area
8544  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8545  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8546  * @cfg {string} html text
8547  * 
8548  * @constructor
8549  * Create a new TextArea
8550  * @param {Object} config The config object
8551  */
8552
8553 Roo.bootstrap.TextArea = function(config){
8554     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8555    
8556 };
8557
8558 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8559      
8560     cols : false,
8561     rows : 5,
8562     readOnly : false,
8563     warp : 'soft',
8564     resize : false,
8565     value: false,
8566     html: false,
8567     
8568     getAutoCreate : function(){
8569         
8570         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8571         
8572         var id = Roo.id();
8573         
8574         var cfg = {};
8575         
8576         var input =  {
8577             tag: 'textarea',
8578             id : id,
8579             warp : this.warp,
8580             rows : this.rows,
8581             value : this.value || '',
8582             html: this.html || '',
8583             cls : 'form-control',
8584             placeholder : this.placeholder || '' 
8585             
8586         };
8587         
8588         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8589             input.maxLength = this.maxLength;
8590         }
8591         
8592         if(this.resize){
8593             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8594         }
8595         
8596         if(this.cols){
8597             input.cols = this.cols;
8598         }
8599         
8600         if (this.readOnly) {
8601             input.readonly = true;
8602         }
8603         
8604         if (this.name) {
8605             input.name = this.name;
8606         }
8607         
8608         if (this.size) {
8609             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8610         }
8611         
8612         var settings=this;
8613         ['xs','sm','md','lg'].map(function(size){
8614             if (settings[size]) {
8615                 cfg.cls += ' col-' + size + '-' + settings[size];
8616             }
8617         });
8618         
8619         var inputblock = input;
8620         
8621         if(this.hasFeedback && !this.allowBlank){
8622             
8623             var feedback = {
8624                 tag: 'span',
8625                 cls: 'glyphicon form-control-feedback'
8626             };
8627
8628             inputblock = {
8629                 cls : 'has-feedback',
8630                 cn :  [
8631                     input,
8632                     feedback
8633                 ] 
8634             };  
8635         }
8636         
8637         
8638         if (this.before || this.after) {
8639             
8640             inputblock = {
8641                 cls : 'input-group',
8642                 cn :  [] 
8643             };
8644             if (this.before) {
8645                 inputblock.cn.push({
8646                     tag :'span',
8647                     cls : 'input-group-addon',
8648                     html : this.before
8649                 });
8650             }
8651             
8652             inputblock.cn.push(input);
8653             
8654             if(this.hasFeedback && !this.allowBlank){
8655                 inputblock.cls += ' has-feedback';
8656                 inputblock.cn.push(feedback);
8657             }
8658             
8659             if (this.after) {
8660                 inputblock.cn.push({
8661                     tag :'span',
8662                     cls : 'input-group-addon',
8663                     html : this.after
8664                 });
8665             }
8666             
8667         }
8668         
8669         if (align ==='left' && this.fieldLabel.length) {
8670                 Roo.log("left and has label");
8671                 cfg.cn = [
8672                     
8673                     {
8674                         tag: 'label',
8675                         'for' :  id,
8676                         cls : 'control-label col-sm-' + this.labelWidth,
8677                         html : this.fieldLabel
8678                         
8679                     },
8680                     {
8681                         cls : "col-sm-" + (12 - this.labelWidth), 
8682                         cn: [
8683                             inputblock
8684                         ]
8685                     }
8686                     
8687                 ];
8688         } else if ( this.fieldLabel.length) {
8689                 Roo.log(" label");
8690                  cfg.cn = [
8691                    
8692                     {
8693                         tag: 'label',
8694                         //cls : 'input-group-addon',
8695                         html : this.fieldLabel
8696                         
8697                     },
8698                     
8699                     inputblock
8700                     
8701                 ];
8702
8703         } else {
8704             
8705                    Roo.log(" no label && no align");
8706                 cfg.cn = [
8707                     
8708                         inputblock
8709                     
8710                 ];
8711                 
8712                 
8713         }
8714         
8715         if (this.disabled) {
8716             input.disabled=true;
8717         }
8718         
8719         return cfg;
8720         
8721     },
8722     /**
8723      * return the real textarea element.
8724      */
8725     inputEl: function ()
8726     {
8727         return this.el.select('textarea.form-control',true).first();
8728     },
8729     
8730     /**
8731      * Clear any invalid styles/messages for this field
8732      */
8733     clearInvalid : function()
8734     {
8735         
8736         if(!this.el || this.preventMark){ // not rendered
8737             return;
8738         }
8739         
8740         var label = this.el.select('label', true).first();
8741         var icon = this.el.select('i.fa-star', true).first();
8742         
8743         if(label && icon){
8744             icon.remove();
8745         }
8746         
8747         this.el.removeClass(this.invalidClass);
8748         
8749         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8750             
8751             var feedback = this.el.select('.form-control-feedback', true).first();
8752             
8753             if(feedback){
8754                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8755             }
8756             
8757         }
8758         
8759         this.fireEvent('valid', this);
8760     },
8761     
8762      /**
8763      * Mark this field as valid
8764      */
8765     markValid : function()
8766     {
8767         if(!this.el  || this.preventMark){ // not rendered
8768             return;
8769         }
8770         
8771         this.el.removeClass([this.invalidClass, this.validClass]);
8772         
8773         var feedback = this.el.select('.form-control-feedback', true).first();
8774             
8775         if(feedback){
8776             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8777         }
8778
8779         if(this.disabled || this.allowBlank){
8780             return;
8781         }
8782         
8783         var label = this.el.select('label', true).first();
8784         var icon = this.el.select('i.fa-star', true).first();
8785         
8786         if(label && icon){
8787             icon.remove();
8788         }
8789         
8790         this.el.addClass(this.validClass);
8791         
8792         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8793             
8794             var feedback = this.el.select('.form-control-feedback', true).first();
8795             
8796             if(feedback){
8797                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8798                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8799             }
8800             
8801         }
8802         
8803         this.fireEvent('valid', this);
8804     },
8805     
8806      /**
8807      * Mark this field as invalid
8808      * @param {String} msg The validation message
8809      */
8810     markInvalid : function(msg)
8811     {
8812         if(!this.el  || this.preventMark){ // not rendered
8813             return;
8814         }
8815         
8816         this.el.removeClass([this.invalidClass, this.validClass]);
8817         
8818         var feedback = this.el.select('.form-control-feedback', true).first();
8819             
8820         if(feedback){
8821             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8822         }
8823
8824         if(this.disabled || this.allowBlank){
8825             return;
8826         }
8827         
8828         var label = this.el.select('label', true).first();
8829         var icon = this.el.select('i.fa-star', true).first();
8830         
8831         if(!this.getValue().length && label && !icon){
8832             this.el.createChild({
8833                 tag : 'i',
8834                 cls : 'text-danger fa fa-lg fa-star',
8835                 tooltip : 'This field is required',
8836                 style : 'margin-right:5px;'
8837             }, label, true);
8838         }
8839
8840         this.el.addClass(this.invalidClass);
8841         
8842         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8843             
8844             var feedback = this.el.select('.form-control-feedback', true).first();
8845             
8846             if(feedback){
8847                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8848                 
8849                 if(this.getValue().length || this.forceFeedback){
8850                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8851                 }
8852                 
8853             }
8854             
8855         }
8856         
8857         this.fireEvent('invalid', this, msg);
8858     }
8859 });
8860
8861  
8862 /*
8863  * - LGPL
8864  *
8865  * trigger field - base class for combo..
8866  * 
8867  */
8868  
8869 /**
8870  * @class Roo.bootstrap.TriggerField
8871  * @extends Roo.bootstrap.Input
8872  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8873  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8874  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8875  * for which you can provide a custom implementation.  For example:
8876  * <pre><code>
8877 var trigger = new Roo.bootstrap.TriggerField();
8878 trigger.onTriggerClick = myTriggerFn;
8879 trigger.applyTo('my-field');
8880 </code></pre>
8881  *
8882  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8883  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8884  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8885  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8886  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8887
8888  * @constructor
8889  * Create a new TriggerField.
8890  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8891  * to the base TextField)
8892  */
8893 Roo.bootstrap.TriggerField = function(config){
8894     this.mimicing = false;
8895     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8896 };
8897
8898 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8899     /**
8900      * @cfg {String} triggerClass A CSS class to apply to the trigger
8901      */
8902      /**
8903      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8904      */
8905     hideTrigger:false,
8906
8907     /**
8908      * @cfg {Boolean} removable (true|false) special filter default false
8909      */
8910     removable : false,
8911     
8912     /** @cfg {Boolean} grow @hide */
8913     /** @cfg {Number} growMin @hide */
8914     /** @cfg {Number} growMax @hide */
8915
8916     /**
8917      * @hide 
8918      * @method
8919      */
8920     autoSize: Roo.emptyFn,
8921     // private
8922     monitorTab : true,
8923     // private
8924     deferHeight : true,
8925
8926     
8927     actionMode : 'wrap',
8928     
8929     caret : false,
8930     
8931     
8932     getAutoCreate : function(){
8933        
8934         var align = this.labelAlign || this.parentLabelAlign();
8935         
8936         var id = Roo.id();
8937         
8938         var cfg = {
8939             cls: 'form-group' //input-group
8940         };
8941         
8942         
8943         var input =  {
8944             tag: 'input',
8945             id : id,
8946             type : this.inputType,
8947             cls : 'form-control',
8948             autocomplete: 'new-password',
8949             placeholder : this.placeholder || '' 
8950             
8951         };
8952         if (this.name) {
8953             input.name = this.name;
8954         }
8955         if (this.size) {
8956             input.cls += ' input-' + this.size;
8957         }
8958         
8959         if (this.disabled) {
8960             input.disabled=true;
8961         }
8962         
8963         var inputblock = input;
8964         
8965         if(this.hasFeedback && !this.allowBlank){
8966             
8967             var feedback = {
8968                 tag: 'span',
8969                 cls: 'glyphicon form-control-feedback'
8970             };
8971             
8972             if(this.removable && !this.editable && !this.tickable){
8973                 inputblock = {
8974                     cls : 'has-feedback',
8975                     cn :  [
8976                         inputblock,
8977                         {
8978                             tag: 'button',
8979                             html : 'x',
8980                             cls : 'roo-combo-removable-btn close'
8981                         },
8982                         feedback
8983                     ] 
8984                 };
8985             } else {
8986                 inputblock = {
8987                     cls : 'has-feedback',
8988                     cn :  [
8989                         inputblock,
8990                         feedback
8991                     ] 
8992                 };
8993             }
8994
8995         } else {
8996             if(this.removable && !this.editable && !this.tickable){
8997                 inputblock = {
8998                     cls : 'roo-removable',
8999                     cn :  [
9000                         inputblock,
9001                         {
9002                             tag: 'button',
9003                             html : 'x',
9004                             cls : 'roo-combo-removable-btn close'
9005                         }
9006                     ] 
9007                 };
9008             }
9009         }
9010         
9011         if (this.before || this.after) {
9012             
9013             inputblock = {
9014                 cls : 'input-group',
9015                 cn :  [] 
9016             };
9017             if (this.before) {
9018                 inputblock.cn.push({
9019                     tag :'span',
9020                     cls : 'input-group-addon',
9021                     html : this.before
9022                 });
9023             }
9024             
9025             inputblock.cn.push(input);
9026             
9027             if(this.hasFeedback && !this.allowBlank){
9028                 inputblock.cls += ' has-feedback';
9029                 inputblock.cn.push(feedback);
9030             }
9031             
9032             if (this.after) {
9033                 inputblock.cn.push({
9034                     tag :'span',
9035                     cls : 'input-group-addon',
9036                     html : this.after
9037                 });
9038             }
9039             
9040         };
9041         
9042         var box = {
9043             tag: 'div',
9044             cn: [
9045                 {
9046                     tag: 'input',
9047                     type : 'hidden',
9048                     cls: 'form-hidden-field'
9049                 },
9050                 inputblock
9051             ]
9052             
9053         };
9054         
9055         if(this.multiple){
9056             Roo.log('multiple');
9057             
9058             box = {
9059                 tag: 'div',
9060                 cn: [
9061                     {
9062                         tag: 'input',
9063                         type : 'hidden',
9064                         cls: 'form-hidden-field'
9065                     },
9066                     {
9067                         tag: 'ul',
9068                         cls: 'select2-choices',
9069                         cn:[
9070                             {
9071                                 tag: 'li',
9072                                 cls: 'select2-search-field',
9073                                 cn: [
9074
9075                                     inputblock
9076                                 ]
9077                             }
9078                         ]
9079                     }
9080                 ]
9081             }
9082         };
9083         
9084         var combobox = {
9085             cls: 'select2-container input-group',
9086             cn: [
9087                 box
9088 //                {
9089 //                    tag: 'ul',
9090 //                    cls: 'typeahead typeahead-long dropdown-menu',
9091 //                    style: 'display:none'
9092 //                }
9093             ]
9094         };
9095         
9096         if(!this.multiple && this.showToggleBtn){
9097             
9098             var caret = {
9099                         tag: 'span',
9100                         cls: 'caret'
9101              };
9102             if (this.caret != false) {
9103                 caret = {
9104                      tag: 'i',
9105                      cls: 'fa fa-' + this.caret
9106                 };
9107                 
9108             }
9109             
9110             combobox.cn.push({
9111                 tag :'span',
9112                 cls : 'input-group-addon btn dropdown-toggle',
9113                 cn : [
9114                     caret,
9115                     {
9116                         tag: 'span',
9117                         cls: 'combobox-clear',
9118                         cn  : [
9119                             {
9120                                 tag : 'i',
9121                                 cls: 'icon-remove'
9122                             }
9123                         ]
9124                     }
9125                 ]
9126
9127             })
9128         }
9129         
9130         if(this.multiple){
9131             combobox.cls += ' select2-container-multi';
9132         }
9133         
9134         if (align ==='left' && this.fieldLabel.length) {
9135             
9136                 Roo.log("left and has label");
9137                 cfg.cn = [
9138                     
9139                     {
9140                         tag: 'label',
9141                         'for' :  id,
9142                         cls : 'control-label col-sm-' + this.labelWidth,
9143                         html : this.fieldLabel
9144                         
9145                     },
9146                     {
9147                         cls : "col-sm-" + (12 - this.labelWidth), 
9148                         cn: [
9149                             combobox
9150                         ]
9151                     }
9152                     
9153                 ];
9154         } else if ( this.fieldLabel.length) {
9155                 Roo.log(" label");
9156                  cfg.cn = [
9157                    
9158                     {
9159                         tag: 'label',
9160                         //cls : 'input-group-addon',
9161                         html : this.fieldLabel
9162                         
9163                     },
9164                     
9165                     combobox
9166                     
9167                 ];
9168
9169         } else {
9170             
9171                 Roo.log(" no label && no align");
9172                 cfg = combobox
9173                      
9174                 
9175         }
9176          
9177         var settings=this;
9178         ['xs','sm','md','lg'].map(function(size){
9179             if (settings[size]) {
9180                 cfg.cls += ' col-' + size + '-' + settings[size];
9181             }
9182         });
9183         Roo.log(cfg);
9184         return cfg;
9185         
9186     },
9187     
9188     
9189     
9190     // private
9191     onResize : function(w, h){
9192 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9193 //        if(typeof w == 'number'){
9194 //            var x = w - this.trigger.getWidth();
9195 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9196 //            this.trigger.setStyle('left', x+'px');
9197 //        }
9198     },
9199
9200     // private
9201     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9202
9203     // private
9204     getResizeEl : function(){
9205         return this.inputEl();
9206     },
9207
9208     // private
9209     getPositionEl : function(){
9210         return this.inputEl();
9211     },
9212
9213     // private
9214     alignErrorIcon : function(){
9215         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9216     },
9217
9218     // private
9219     initEvents : function(){
9220         
9221         this.createList();
9222         
9223         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9224         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9225         if(!this.multiple && this.showToggleBtn){
9226             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9227             if(this.hideTrigger){
9228                 this.trigger.setDisplayed(false);
9229             }
9230             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9231         }
9232         
9233         if(this.multiple){
9234             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9235         }
9236         
9237         if(this.removable && !this.editable && !this.tickable){
9238             var close = this.closeTriggerEl();
9239             
9240             if(close){
9241                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9242                 close.on('click', this.removeBtnClick, this, close);
9243             }
9244         }
9245         
9246         //this.trigger.addClassOnOver('x-form-trigger-over');
9247         //this.trigger.addClassOnClick('x-form-trigger-click');
9248         
9249         //if(!this.width){
9250         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9251         //}
9252     },
9253     
9254     closeTriggerEl : function()
9255     {
9256         var close = this.el.select('.roo-combo-removable-btn', true).first();
9257         return close ? close : false;
9258     },
9259     
9260     removeBtnClick : function(e, h, el)
9261     {
9262         e.preventDefault();
9263         
9264         if(this.fireEvent("remove", this) !== false){
9265             this.reset();
9266         }
9267     },
9268     
9269     createList : function()
9270     {
9271         this.list = Roo.get(document.body).createChild({
9272             tag: 'ul',
9273             cls: 'typeahead typeahead-long dropdown-menu',
9274             style: 'display:none'
9275         });
9276         
9277         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9278         
9279     },
9280
9281     // private
9282     initTrigger : function(){
9283        
9284     },
9285
9286     // private
9287     onDestroy : function(){
9288         if(this.trigger){
9289             this.trigger.removeAllListeners();
9290           //  this.trigger.remove();
9291         }
9292         //if(this.wrap){
9293         //    this.wrap.remove();
9294         //}
9295         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9296     },
9297
9298     // private
9299     onFocus : function(){
9300         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9301         /*
9302         if(!this.mimicing){
9303             this.wrap.addClass('x-trigger-wrap-focus');
9304             this.mimicing = true;
9305             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9306             if(this.monitorTab){
9307                 this.el.on("keydown", this.checkTab, this);
9308             }
9309         }
9310         */
9311     },
9312
9313     // private
9314     checkTab : function(e){
9315         if(e.getKey() == e.TAB){
9316             this.triggerBlur();
9317         }
9318     },
9319
9320     // private
9321     onBlur : function(){
9322         // do nothing
9323     },
9324
9325     // private
9326     mimicBlur : function(e, t){
9327         /*
9328         if(!this.wrap.contains(t) && this.validateBlur()){
9329             this.triggerBlur();
9330         }
9331         */
9332     },
9333
9334     // private
9335     triggerBlur : function(){
9336         this.mimicing = false;
9337         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9338         if(this.monitorTab){
9339             this.el.un("keydown", this.checkTab, this);
9340         }
9341         //this.wrap.removeClass('x-trigger-wrap-focus');
9342         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9343     },
9344
9345     // private
9346     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9347     validateBlur : function(e, t){
9348         return true;
9349     },
9350
9351     // private
9352     onDisable : function(){
9353         this.inputEl().dom.disabled = true;
9354         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9355         //if(this.wrap){
9356         //    this.wrap.addClass('x-item-disabled');
9357         //}
9358     },
9359
9360     // private
9361     onEnable : function(){
9362         this.inputEl().dom.disabled = false;
9363         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9364         //if(this.wrap){
9365         //    this.el.removeClass('x-item-disabled');
9366         //}
9367     },
9368
9369     // private
9370     onShow : function(){
9371         var ae = this.getActionEl();
9372         
9373         if(ae){
9374             ae.dom.style.display = '';
9375             ae.dom.style.visibility = 'visible';
9376         }
9377     },
9378
9379     // private
9380     
9381     onHide : function(){
9382         var ae = this.getActionEl();
9383         ae.dom.style.display = 'none';
9384     },
9385
9386     /**
9387      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9388      * by an implementing function.
9389      * @method
9390      * @param {EventObject} e
9391      */
9392     onTriggerClick : Roo.emptyFn
9393 });
9394  /*
9395  * Based on:
9396  * Ext JS Library 1.1.1
9397  * Copyright(c) 2006-2007, Ext JS, LLC.
9398  *
9399  * Originally Released Under LGPL - original licence link has changed is not relivant.
9400  *
9401  * Fork - LGPL
9402  * <script type="text/javascript">
9403  */
9404
9405
9406 /**
9407  * @class Roo.data.SortTypes
9408  * @singleton
9409  * Defines the default sorting (casting?) comparison functions used when sorting data.
9410  */
9411 Roo.data.SortTypes = {
9412     /**
9413      * Default sort that does nothing
9414      * @param {Mixed} s The value being converted
9415      * @return {Mixed} The comparison value
9416      */
9417     none : function(s){
9418         return s;
9419     },
9420     
9421     /**
9422      * The regular expression used to strip tags
9423      * @type {RegExp}
9424      * @property
9425      */
9426     stripTagsRE : /<\/?[^>]+>/gi,
9427     
9428     /**
9429      * Strips all HTML tags to sort on text only
9430      * @param {Mixed} s The value being converted
9431      * @return {String} The comparison value
9432      */
9433     asText : function(s){
9434         return String(s).replace(this.stripTagsRE, "");
9435     },
9436     
9437     /**
9438      * Strips all HTML tags to sort on text only - Case insensitive
9439      * @param {Mixed} s The value being converted
9440      * @return {String} The comparison value
9441      */
9442     asUCText : function(s){
9443         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9444     },
9445     
9446     /**
9447      * Case insensitive string
9448      * @param {Mixed} s The value being converted
9449      * @return {String} The comparison value
9450      */
9451     asUCString : function(s) {
9452         return String(s).toUpperCase();
9453     },
9454     
9455     /**
9456      * Date sorting
9457      * @param {Mixed} s The value being converted
9458      * @return {Number} The comparison value
9459      */
9460     asDate : function(s) {
9461         if(!s){
9462             return 0;
9463         }
9464         if(s instanceof Date){
9465             return s.getTime();
9466         }
9467         return Date.parse(String(s));
9468     },
9469     
9470     /**
9471      * Float sorting
9472      * @param {Mixed} s The value being converted
9473      * @return {Float} The comparison value
9474      */
9475     asFloat : function(s) {
9476         var val = parseFloat(String(s).replace(/,/g, ""));
9477         if(isNaN(val)) {
9478             val = 0;
9479         }
9480         return val;
9481     },
9482     
9483     /**
9484      * Integer sorting
9485      * @param {Mixed} s The value being converted
9486      * @return {Number} The comparison value
9487      */
9488     asInt : function(s) {
9489         var val = parseInt(String(s).replace(/,/g, ""));
9490         if(isNaN(val)) {
9491             val = 0;
9492         }
9493         return val;
9494     }
9495 };/*
9496  * Based on:
9497  * Ext JS Library 1.1.1
9498  * Copyright(c) 2006-2007, Ext JS, LLC.
9499  *
9500  * Originally Released Under LGPL - original licence link has changed is not relivant.
9501  *
9502  * Fork - LGPL
9503  * <script type="text/javascript">
9504  */
9505
9506 /**
9507 * @class Roo.data.Record
9508  * Instances of this class encapsulate both record <em>definition</em> information, and record
9509  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9510  * to access Records cached in an {@link Roo.data.Store} object.<br>
9511  * <p>
9512  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9513  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9514  * objects.<br>
9515  * <p>
9516  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9517  * @constructor
9518  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9519  * {@link #create}. The parameters are the same.
9520  * @param {Array} data An associative Array of data values keyed by the field name.
9521  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9522  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9523  * not specified an integer id is generated.
9524  */
9525 Roo.data.Record = function(data, id){
9526     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9527     this.data = data;
9528 };
9529
9530 /**
9531  * Generate a constructor for a specific record layout.
9532  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9533  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9534  * Each field definition object may contain the following properties: <ul>
9535  * <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,
9536  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9537  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9538  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9539  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9540  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9541  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9542  * this may be omitted.</p></li>
9543  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9544  * <ul><li>auto (Default, implies no conversion)</li>
9545  * <li>string</li>
9546  * <li>int</li>
9547  * <li>float</li>
9548  * <li>boolean</li>
9549  * <li>date</li></ul></p></li>
9550  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9551  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9552  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9553  * by the Reader into an object that will be stored in the Record. It is passed the
9554  * following parameters:<ul>
9555  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9556  * </ul></p></li>
9557  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9558  * </ul>
9559  * <br>usage:<br><pre><code>
9560 var TopicRecord = Roo.data.Record.create(
9561     {name: 'title', mapping: 'topic_title'},
9562     {name: 'author', mapping: 'username'},
9563     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9564     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9565     {name: 'lastPoster', mapping: 'user2'},
9566     {name: 'excerpt', mapping: 'post_text'}
9567 );
9568
9569 var myNewRecord = new TopicRecord({
9570     title: 'Do my job please',
9571     author: 'noobie',
9572     totalPosts: 1,
9573     lastPost: new Date(),
9574     lastPoster: 'Animal',
9575     excerpt: 'No way dude!'
9576 });
9577 myStore.add(myNewRecord);
9578 </code></pre>
9579  * @method create
9580  * @static
9581  */
9582 Roo.data.Record.create = function(o){
9583     var f = function(){
9584         f.superclass.constructor.apply(this, arguments);
9585     };
9586     Roo.extend(f, Roo.data.Record);
9587     var p = f.prototype;
9588     p.fields = new Roo.util.MixedCollection(false, function(field){
9589         return field.name;
9590     });
9591     for(var i = 0, len = o.length; i < len; i++){
9592         p.fields.add(new Roo.data.Field(o[i]));
9593     }
9594     f.getField = function(name){
9595         return p.fields.get(name);  
9596     };
9597     return f;
9598 };
9599
9600 Roo.data.Record.AUTO_ID = 1000;
9601 Roo.data.Record.EDIT = 'edit';
9602 Roo.data.Record.REJECT = 'reject';
9603 Roo.data.Record.COMMIT = 'commit';
9604
9605 Roo.data.Record.prototype = {
9606     /**
9607      * Readonly flag - true if this record has been modified.
9608      * @type Boolean
9609      */
9610     dirty : false,
9611     editing : false,
9612     error: null,
9613     modified: null,
9614
9615     // private
9616     join : function(store){
9617         this.store = store;
9618     },
9619
9620     /**
9621      * Set the named field to the specified value.
9622      * @param {String} name The name of the field to set.
9623      * @param {Object} value The value to set the field to.
9624      */
9625     set : function(name, value){
9626         if(this.data[name] == value){
9627             return;
9628         }
9629         this.dirty = true;
9630         if(!this.modified){
9631             this.modified = {};
9632         }
9633         if(typeof this.modified[name] == 'undefined'){
9634             this.modified[name] = this.data[name];
9635         }
9636         this.data[name] = value;
9637         if(!this.editing && this.store){
9638             this.store.afterEdit(this);
9639         }       
9640     },
9641
9642     /**
9643      * Get the value of the named field.
9644      * @param {String} name The name of the field to get the value of.
9645      * @return {Object} The value of the field.
9646      */
9647     get : function(name){
9648         return this.data[name]; 
9649     },
9650
9651     // private
9652     beginEdit : function(){
9653         this.editing = true;
9654         this.modified = {}; 
9655     },
9656
9657     // private
9658     cancelEdit : function(){
9659         this.editing = false;
9660         delete this.modified;
9661     },
9662
9663     // private
9664     endEdit : function(){
9665         this.editing = false;
9666         if(this.dirty && this.store){
9667             this.store.afterEdit(this);
9668         }
9669     },
9670
9671     /**
9672      * Usually called by the {@link Roo.data.Store} which owns the Record.
9673      * Rejects all changes made to the Record since either creation, or the last commit operation.
9674      * Modified fields are reverted to their original values.
9675      * <p>
9676      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9677      * of reject operations.
9678      */
9679     reject : function(){
9680         var m = this.modified;
9681         for(var n in m){
9682             if(typeof m[n] != "function"){
9683                 this.data[n] = m[n];
9684             }
9685         }
9686         this.dirty = false;
9687         delete this.modified;
9688         this.editing = false;
9689         if(this.store){
9690             this.store.afterReject(this);
9691         }
9692     },
9693
9694     /**
9695      * Usually called by the {@link Roo.data.Store} which owns the Record.
9696      * Commits all changes made to the Record since either creation, or the last commit operation.
9697      * <p>
9698      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9699      * of commit operations.
9700      */
9701     commit : function(){
9702         this.dirty = false;
9703         delete this.modified;
9704         this.editing = false;
9705         if(this.store){
9706             this.store.afterCommit(this);
9707         }
9708     },
9709
9710     // private
9711     hasError : function(){
9712         return this.error != null;
9713     },
9714
9715     // private
9716     clearError : function(){
9717         this.error = null;
9718     },
9719
9720     /**
9721      * Creates a copy of this record.
9722      * @param {String} id (optional) A new record id if you don't want to use this record's id
9723      * @return {Record}
9724      */
9725     copy : function(newId) {
9726         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9727     }
9728 };/*
9729  * Based on:
9730  * Ext JS Library 1.1.1
9731  * Copyright(c) 2006-2007, Ext JS, LLC.
9732  *
9733  * Originally Released Under LGPL - original licence link has changed is not relivant.
9734  *
9735  * Fork - LGPL
9736  * <script type="text/javascript">
9737  */
9738
9739
9740
9741 /**
9742  * @class Roo.data.Store
9743  * @extends Roo.util.Observable
9744  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9745  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9746  * <p>
9747  * 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
9748  * has no knowledge of the format of the data returned by the Proxy.<br>
9749  * <p>
9750  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9751  * instances from the data object. These records are cached and made available through accessor functions.
9752  * @constructor
9753  * Creates a new Store.
9754  * @param {Object} config A config object containing the objects needed for the Store to access data,
9755  * and read the data into Records.
9756  */
9757 Roo.data.Store = function(config){
9758     this.data = new Roo.util.MixedCollection(false);
9759     this.data.getKey = function(o){
9760         return o.id;
9761     };
9762     this.baseParams = {};
9763     // private
9764     this.paramNames = {
9765         "start" : "start",
9766         "limit" : "limit",
9767         "sort" : "sort",
9768         "dir" : "dir",
9769         "multisort" : "_multisort"
9770     };
9771
9772     if(config && config.data){
9773         this.inlineData = config.data;
9774         delete config.data;
9775     }
9776
9777     Roo.apply(this, config);
9778     
9779     if(this.reader){ // reader passed
9780         this.reader = Roo.factory(this.reader, Roo.data);
9781         this.reader.xmodule = this.xmodule || false;
9782         if(!this.recordType){
9783             this.recordType = this.reader.recordType;
9784         }
9785         if(this.reader.onMetaChange){
9786             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9787         }
9788     }
9789
9790     if(this.recordType){
9791         this.fields = this.recordType.prototype.fields;
9792     }
9793     this.modified = [];
9794
9795     this.addEvents({
9796         /**
9797          * @event datachanged
9798          * Fires when the data cache has changed, and a widget which is using this Store
9799          * as a Record cache should refresh its view.
9800          * @param {Store} this
9801          */
9802         datachanged : true,
9803         /**
9804          * @event metachange
9805          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9806          * @param {Store} this
9807          * @param {Object} meta The JSON metadata
9808          */
9809         metachange : true,
9810         /**
9811          * @event add
9812          * Fires when Records have been added to the Store
9813          * @param {Store} this
9814          * @param {Roo.data.Record[]} records The array of Records added
9815          * @param {Number} index The index at which the record(s) were added
9816          */
9817         add : true,
9818         /**
9819          * @event remove
9820          * Fires when a Record has been removed from the Store
9821          * @param {Store} this
9822          * @param {Roo.data.Record} record The Record that was removed
9823          * @param {Number} index The index at which the record was removed
9824          */
9825         remove : true,
9826         /**
9827          * @event update
9828          * Fires when a Record has been updated
9829          * @param {Store} this
9830          * @param {Roo.data.Record} record The Record that was updated
9831          * @param {String} operation The update operation being performed.  Value may be one of:
9832          * <pre><code>
9833  Roo.data.Record.EDIT
9834  Roo.data.Record.REJECT
9835  Roo.data.Record.COMMIT
9836          * </code></pre>
9837          */
9838         update : true,
9839         /**
9840          * @event clear
9841          * Fires when the data cache has been cleared.
9842          * @param {Store} this
9843          */
9844         clear : true,
9845         /**
9846          * @event beforeload
9847          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9848          * the load action will be canceled.
9849          * @param {Store} this
9850          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9851          */
9852         beforeload : true,
9853         /**
9854          * @event beforeloadadd
9855          * Fires after a new set of Records has been loaded.
9856          * @param {Store} this
9857          * @param {Roo.data.Record[]} records The Records that were loaded
9858          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9859          */
9860         beforeloadadd : true,
9861         /**
9862          * @event load
9863          * Fires after a new set of Records has been loaded, before they are added to the store.
9864          * @param {Store} this
9865          * @param {Roo.data.Record[]} records The Records that were loaded
9866          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9867          * @params {Object} return from reader
9868          */
9869         load : true,
9870         /**
9871          * @event loadexception
9872          * Fires if an exception occurs in the Proxy during loading.
9873          * Called with the signature of the Proxy's "loadexception" event.
9874          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9875          * 
9876          * @param {Proxy} 
9877          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9878          * @param {Object} load options 
9879          * @param {Object} jsonData from your request (normally this contains the Exception)
9880          */
9881         loadexception : true
9882     });
9883     
9884     if(this.proxy){
9885         this.proxy = Roo.factory(this.proxy, Roo.data);
9886         this.proxy.xmodule = this.xmodule || false;
9887         this.relayEvents(this.proxy,  ["loadexception"]);
9888     }
9889     this.sortToggle = {};
9890     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9891
9892     Roo.data.Store.superclass.constructor.call(this);
9893
9894     if(this.inlineData){
9895         this.loadData(this.inlineData);
9896         delete this.inlineData;
9897     }
9898 };
9899
9900 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9901      /**
9902     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9903     * without a remote query - used by combo/forms at present.
9904     */
9905     
9906     /**
9907     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9908     */
9909     /**
9910     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9911     */
9912     /**
9913     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9914     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9915     */
9916     /**
9917     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9918     * on any HTTP request
9919     */
9920     /**
9921     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9922     */
9923     /**
9924     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9925     */
9926     multiSort: false,
9927     /**
9928     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9929     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9930     */
9931     remoteSort : false,
9932
9933     /**
9934     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9935      * loaded or when a record is removed. (defaults to false).
9936     */
9937     pruneModifiedRecords : false,
9938
9939     // private
9940     lastOptions : null,
9941
9942     /**
9943      * Add Records to the Store and fires the add event.
9944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9945      */
9946     add : function(records){
9947         records = [].concat(records);
9948         for(var i = 0, len = records.length; i < len; i++){
9949             records[i].join(this);
9950         }
9951         var index = this.data.length;
9952         this.data.addAll(records);
9953         this.fireEvent("add", this, records, index);
9954     },
9955
9956     /**
9957      * Remove a Record from the Store and fires the remove event.
9958      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9959      */
9960     remove : function(record){
9961         var index = this.data.indexOf(record);
9962         this.data.removeAt(index);
9963         if(this.pruneModifiedRecords){
9964             this.modified.remove(record);
9965         }
9966         this.fireEvent("remove", this, record, index);
9967     },
9968
9969     /**
9970      * Remove all Records from the Store and fires the clear event.
9971      */
9972     removeAll : function(){
9973         this.data.clear();
9974         if(this.pruneModifiedRecords){
9975             this.modified = [];
9976         }
9977         this.fireEvent("clear", this);
9978     },
9979
9980     /**
9981      * Inserts Records to the Store at the given index and fires the add event.
9982      * @param {Number} index The start index at which to insert the passed Records.
9983      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9984      */
9985     insert : function(index, records){
9986         records = [].concat(records);
9987         for(var i = 0, len = records.length; i < len; i++){
9988             this.data.insert(index, records[i]);
9989             records[i].join(this);
9990         }
9991         this.fireEvent("add", this, records, index);
9992     },
9993
9994     /**
9995      * Get the index within the cache of the passed Record.
9996      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9997      * @return {Number} The index of the passed Record. Returns -1 if not found.
9998      */
9999     indexOf : function(record){
10000         return this.data.indexOf(record);
10001     },
10002
10003     /**
10004      * Get the index within the cache of the Record with the passed id.
10005      * @param {String} id The id of the Record to find.
10006      * @return {Number} The index of the Record. Returns -1 if not found.
10007      */
10008     indexOfId : function(id){
10009         return this.data.indexOfKey(id);
10010     },
10011
10012     /**
10013      * Get the Record with the specified id.
10014      * @param {String} id The id of the Record to find.
10015      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10016      */
10017     getById : function(id){
10018         return this.data.key(id);
10019     },
10020
10021     /**
10022      * Get the Record at the specified index.
10023      * @param {Number} index The index of the Record to find.
10024      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10025      */
10026     getAt : function(index){
10027         return this.data.itemAt(index);
10028     },
10029
10030     /**
10031      * Returns a range of Records between specified indices.
10032      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10033      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10034      * @return {Roo.data.Record[]} An array of Records
10035      */
10036     getRange : function(start, end){
10037         return this.data.getRange(start, end);
10038     },
10039
10040     // private
10041     storeOptions : function(o){
10042         o = Roo.apply({}, o);
10043         delete o.callback;
10044         delete o.scope;
10045         this.lastOptions = o;
10046     },
10047
10048     /**
10049      * Loads the Record cache from the configured Proxy using the configured Reader.
10050      * <p>
10051      * If using remote paging, then the first load call must specify the <em>start</em>
10052      * and <em>limit</em> properties in the options.params property to establish the initial
10053      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10054      * <p>
10055      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10056      * and this call will return before the new data has been loaded. Perform any post-processing
10057      * in a callback function, or in a "load" event handler.</strong>
10058      * <p>
10059      * @param {Object} options An object containing properties which control loading options:<ul>
10060      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10061      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10062      * passed the following arguments:<ul>
10063      * <li>r : Roo.data.Record[]</li>
10064      * <li>options: Options object from the load call</li>
10065      * <li>success: Boolean success indicator</li></ul></li>
10066      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10067      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10068      * </ul>
10069      */
10070     load : function(options){
10071         options = options || {};
10072         if(this.fireEvent("beforeload", this, options) !== false){
10073             this.storeOptions(options);
10074             var p = Roo.apply(options.params || {}, this.baseParams);
10075             // if meta was not loaded from remote source.. try requesting it.
10076             if (!this.reader.metaFromRemote) {
10077                 p._requestMeta = 1;
10078             }
10079             if(this.sortInfo && this.remoteSort){
10080                 var pn = this.paramNames;
10081                 p[pn["sort"]] = this.sortInfo.field;
10082                 p[pn["dir"]] = this.sortInfo.direction;
10083             }
10084             if (this.multiSort) {
10085                 var pn = this.paramNames;
10086                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10087             }
10088             
10089             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10090         }
10091     },
10092
10093     /**
10094      * Reloads the Record cache from the configured Proxy using the configured Reader and
10095      * the options from the last load operation performed.
10096      * @param {Object} options (optional) An object containing properties which may override the options
10097      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10098      * the most recently used options are reused).
10099      */
10100     reload : function(options){
10101         this.load(Roo.applyIf(options||{}, this.lastOptions));
10102     },
10103
10104     // private
10105     // Called as a callback by the Reader during a load operation.
10106     loadRecords : function(o, options, success){
10107         if(!o || success === false){
10108             if(success !== false){
10109                 this.fireEvent("load", this, [], options, o);
10110             }
10111             if(options.callback){
10112                 options.callback.call(options.scope || this, [], options, false);
10113             }
10114             return;
10115         }
10116         // if data returned failure - throw an exception.
10117         if (o.success === false) {
10118             // show a message if no listener is registered.
10119             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10120                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10121             }
10122             // loadmask wil be hooked into this..
10123             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10124             return;
10125         }
10126         var r = o.records, t = o.totalRecords || r.length;
10127         
10128         this.fireEvent("beforeloadadd", this, r, options, o);
10129         
10130         if(!options || options.add !== true){
10131             if(this.pruneModifiedRecords){
10132                 this.modified = [];
10133             }
10134             for(var i = 0, len = r.length; i < len; i++){
10135                 r[i].join(this);
10136             }
10137             if(this.snapshot){
10138                 this.data = this.snapshot;
10139                 delete this.snapshot;
10140             }
10141             this.data.clear();
10142             this.data.addAll(r);
10143             this.totalLength = t;
10144             this.applySort();
10145             this.fireEvent("datachanged", this);
10146         }else{
10147             this.totalLength = Math.max(t, this.data.length+r.length);
10148             this.add(r);
10149         }
10150         this.fireEvent("load", this, r, options, o);
10151         if(options.callback){
10152             options.callback.call(options.scope || this, r, options, true);
10153         }
10154     },
10155
10156
10157     /**
10158      * Loads data from a passed data block. A Reader which understands the format of the data
10159      * must have been configured in the constructor.
10160      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10161      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10162      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10163      */
10164     loadData : function(o, append){
10165         var r = this.reader.readRecords(o);
10166         this.loadRecords(r, {add: append}, true);
10167     },
10168
10169     /**
10170      * Gets the number of cached records.
10171      * <p>
10172      * <em>If using paging, this may not be the total size of the dataset. If the data object
10173      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10174      * the data set size</em>
10175      */
10176     getCount : function(){
10177         return this.data.length || 0;
10178     },
10179
10180     /**
10181      * Gets the total number of records in the dataset as returned by the server.
10182      * <p>
10183      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10184      * the dataset size</em>
10185      */
10186     getTotalCount : function(){
10187         return this.totalLength || 0;
10188     },
10189
10190     /**
10191      * Returns the sort state of the Store as an object with two properties:
10192      * <pre><code>
10193  field {String} The name of the field by which the Records are sorted
10194  direction {String} The sort order, "ASC" or "DESC"
10195      * </code></pre>
10196      */
10197     getSortState : function(){
10198         return this.sortInfo;
10199     },
10200
10201     // private
10202     applySort : function(){
10203         if(this.sortInfo && !this.remoteSort){
10204             var s = this.sortInfo, f = s.field;
10205             var st = this.fields.get(f).sortType;
10206             var fn = function(r1, r2){
10207                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10208                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10209             };
10210             this.data.sort(s.direction, fn);
10211             if(this.snapshot && this.snapshot != this.data){
10212                 this.snapshot.sort(s.direction, fn);
10213             }
10214         }
10215     },
10216
10217     /**
10218      * Sets the default sort column and order to be used by the next load operation.
10219      * @param {String} fieldName The name of the field to sort by.
10220      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10221      */
10222     setDefaultSort : function(field, dir){
10223         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10224     },
10225
10226     /**
10227      * Sort the Records.
10228      * If remote sorting is used, the sort is performed on the server, and the cache is
10229      * reloaded. If local sorting is used, the cache is sorted internally.
10230      * @param {String} fieldName The name of the field to sort by.
10231      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10232      */
10233     sort : function(fieldName, dir){
10234         var f = this.fields.get(fieldName);
10235         if(!dir){
10236             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10237             
10238             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10239                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10240             }else{
10241                 dir = f.sortDir;
10242             }
10243         }
10244         this.sortToggle[f.name] = dir;
10245         this.sortInfo = {field: f.name, direction: dir};
10246         if(!this.remoteSort){
10247             this.applySort();
10248             this.fireEvent("datachanged", this);
10249         }else{
10250             this.load(this.lastOptions);
10251         }
10252     },
10253
10254     /**
10255      * Calls the specified function for each of the Records in the cache.
10256      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10257      * Returning <em>false</em> aborts and exits the iteration.
10258      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10259      */
10260     each : function(fn, scope){
10261         this.data.each(fn, scope);
10262     },
10263
10264     /**
10265      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10266      * (e.g., during paging).
10267      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10268      */
10269     getModifiedRecords : function(){
10270         return this.modified;
10271     },
10272
10273     // private
10274     createFilterFn : function(property, value, anyMatch){
10275         if(!value.exec){ // not a regex
10276             value = String(value);
10277             if(value.length == 0){
10278                 return false;
10279             }
10280             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10281         }
10282         return function(r){
10283             return value.test(r.data[property]);
10284         };
10285     },
10286
10287     /**
10288      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10289      * @param {String} property A field on your records
10290      * @param {Number} start The record index to start at (defaults to 0)
10291      * @param {Number} end The last record index to include (defaults to length - 1)
10292      * @return {Number} The sum
10293      */
10294     sum : function(property, start, end){
10295         var rs = this.data.items, v = 0;
10296         start = start || 0;
10297         end = (end || end === 0) ? end : rs.length-1;
10298
10299         for(var i = start; i <= end; i++){
10300             v += (rs[i].data[property] || 0);
10301         }
10302         return v;
10303     },
10304
10305     /**
10306      * Filter the records by a specified property.
10307      * @param {String} field A field on your records
10308      * @param {String/RegExp} value Either a string that the field
10309      * should start with or a RegExp to test against the field
10310      * @param {Boolean} anyMatch True to match any part not just the beginning
10311      */
10312     filter : function(property, value, anyMatch){
10313         var fn = this.createFilterFn(property, value, anyMatch);
10314         return fn ? this.filterBy(fn) : this.clearFilter();
10315     },
10316
10317     /**
10318      * Filter by a function. The specified function will be called with each
10319      * record in this data source. If the function returns true the record is included,
10320      * otherwise it is filtered.
10321      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10322      * @param {Object} scope (optional) The scope of the function (defaults to this)
10323      */
10324     filterBy : function(fn, scope){
10325         this.snapshot = this.snapshot || this.data;
10326         this.data = this.queryBy(fn, scope||this);
10327         this.fireEvent("datachanged", this);
10328     },
10329
10330     /**
10331      * Query the records by a specified property.
10332      * @param {String} field A field on your records
10333      * @param {String/RegExp} value Either a string that the field
10334      * should start with or a RegExp to test against the field
10335      * @param {Boolean} anyMatch True to match any part not just the beginning
10336      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10337      */
10338     query : function(property, value, anyMatch){
10339         var fn = this.createFilterFn(property, value, anyMatch);
10340         return fn ? this.queryBy(fn) : this.data.clone();
10341     },
10342
10343     /**
10344      * Query by a function. The specified function will be called with each
10345      * record in this data source. If the function returns true the record is included
10346      * in the results.
10347      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10348      * @param {Object} scope (optional) The scope of the function (defaults to this)
10349       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10350      **/
10351     queryBy : function(fn, scope){
10352         var data = this.snapshot || this.data;
10353         return data.filterBy(fn, scope||this);
10354     },
10355
10356     /**
10357      * Collects unique values for a particular dataIndex from this store.
10358      * @param {String} dataIndex The property to collect
10359      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10360      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10361      * @return {Array} An array of the unique values
10362      **/
10363     collect : function(dataIndex, allowNull, bypassFilter){
10364         var d = (bypassFilter === true && this.snapshot) ?
10365                 this.snapshot.items : this.data.items;
10366         var v, sv, r = [], l = {};
10367         for(var i = 0, len = d.length; i < len; i++){
10368             v = d[i].data[dataIndex];
10369             sv = String(v);
10370             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10371                 l[sv] = true;
10372                 r[r.length] = v;
10373             }
10374         }
10375         return r;
10376     },
10377
10378     /**
10379      * Revert to a view of the Record cache with no filtering applied.
10380      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10381      */
10382     clearFilter : function(suppressEvent){
10383         if(this.snapshot && this.snapshot != this.data){
10384             this.data = this.snapshot;
10385             delete this.snapshot;
10386             if(suppressEvent !== true){
10387                 this.fireEvent("datachanged", this);
10388             }
10389         }
10390     },
10391
10392     // private
10393     afterEdit : function(record){
10394         if(this.modified.indexOf(record) == -1){
10395             this.modified.push(record);
10396         }
10397         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10398     },
10399     
10400     // private
10401     afterReject : function(record){
10402         this.modified.remove(record);
10403         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10404     },
10405
10406     // private
10407     afterCommit : function(record){
10408         this.modified.remove(record);
10409         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10410     },
10411
10412     /**
10413      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10414      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10415      */
10416     commitChanges : function(){
10417         var m = this.modified.slice(0);
10418         this.modified = [];
10419         for(var i = 0, len = m.length; i < len; i++){
10420             m[i].commit();
10421         }
10422     },
10423
10424     /**
10425      * Cancel outstanding changes on all changed records.
10426      */
10427     rejectChanges : function(){
10428         var m = this.modified.slice(0);
10429         this.modified = [];
10430         for(var i = 0, len = m.length; i < len; i++){
10431             m[i].reject();
10432         }
10433     },
10434
10435     onMetaChange : function(meta, rtype, o){
10436         this.recordType = rtype;
10437         this.fields = rtype.prototype.fields;
10438         delete this.snapshot;
10439         this.sortInfo = meta.sortInfo || this.sortInfo;
10440         this.modified = [];
10441         this.fireEvent('metachange', this, this.reader.meta);
10442     },
10443     
10444     moveIndex : function(data, type)
10445     {
10446         var index = this.indexOf(data);
10447         
10448         var newIndex = index + type;
10449         
10450         this.remove(data);
10451         
10452         this.insert(newIndex, data);
10453         
10454     }
10455 });/*
10456  * Based on:
10457  * Ext JS Library 1.1.1
10458  * Copyright(c) 2006-2007, Ext JS, LLC.
10459  *
10460  * Originally Released Under LGPL - original licence link has changed is not relivant.
10461  *
10462  * Fork - LGPL
10463  * <script type="text/javascript">
10464  */
10465
10466 /**
10467  * @class Roo.data.SimpleStore
10468  * @extends Roo.data.Store
10469  * Small helper class to make creating Stores from Array data easier.
10470  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10471  * @cfg {Array} fields An array of field definition objects, or field name strings.
10472  * @cfg {Array} data The multi-dimensional array of data
10473  * @constructor
10474  * @param {Object} config
10475  */
10476 Roo.data.SimpleStore = function(config){
10477     Roo.data.SimpleStore.superclass.constructor.call(this, {
10478         isLocal : true,
10479         reader: new Roo.data.ArrayReader({
10480                 id: config.id
10481             },
10482             Roo.data.Record.create(config.fields)
10483         ),
10484         proxy : new Roo.data.MemoryProxy(config.data)
10485     });
10486     this.load();
10487 };
10488 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10489  * Based on:
10490  * Ext JS Library 1.1.1
10491  * Copyright(c) 2006-2007, Ext JS, LLC.
10492  *
10493  * Originally Released Under LGPL - original licence link has changed is not relivant.
10494  *
10495  * Fork - LGPL
10496  * <script type="text/javascript">
10497  */
10498
10499 /**
10500 /**
10501  * @extends Roo.data.Store
10502  * @class Roo.data.JsonStore
10503  * Small helper class to make creating Stores for JSON data easier. <br/>
10504 <pre><code>
10505 var store = new Roo.data.JsonStore({
10506     url: 'get-images.php',
10507     root: 'images',
10508     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10509 });
10510 </code></pre>
10511  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10512  * JsonReader and HttpProxy (unless inline data is provided).</b>
10513  * @cfg {Array} fields An array of field definition objects, or field name strings.
10514  * @constructor
10515  * @param {Object} config
10516  */
10517 Roo.data.JsonStore = function(c){
10518     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10519         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10520         reader: new Roo.data.JsonReader(c, c.fields)
10521     }));
10522 };
10523 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10524  * Based on:
10525  * Ext JS Library 1.1.1
10526  * Copyright(c) 2006-2007, Ext JS, LLC.
10527  *
10528  * Originally Released Under LGPL - original licence link has changed is not relivant.
10529  *
10530  * Fork - LGPL
10531  * <script type="text/javascript">
10532  */
10533
10534  
10535 Roo.data.Field = function(config){
10536     if(typeof config == "string"){
10537         config = {name: config};
10538     }
10539     Roo.apply(this, config);
10540     
10541     if(!this.type){
10542         this.type = "auto";
10543     }
10544     
10545     var st = Roo.data.SortTypes;
10546     // named sortTypes are supported, here we look them up
10547     if(typeof this.sortType == "string"){
10548         this.sortType = st[this.sortType];
10549     }
10550     
10551     // set default sortType for strings and dates
10552     if(!this.sortType){
10553         switch(this.type){
10554             case "string":
10555                 this.sortType = st.asUCString;
10556                 break;
10557             case "date":
10558                 this.sortType = st.asDate;
10559                 break;
10560             default:
10561                 this.sortType = st.none;
10562         }
10563     }
10564
10565     // define once
10566     var stripRe = /[\$,%]/g;
10567
10568     // prebuilt conversion function for this field, instead of
10569     // switching every time we're reading a value
10570     if(!this.convert){
10571         var cv, dateFormat = this.dateFormat;
10572         switch(this.type){
10573             case "":
10574             case "auto":
10575             case undefined:
10576                 cv = function(v){ return v; };
10577                 break;
10578             case "string":
10579                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10580                 break;
10581             case "int":
10582                 cv = function(v){
10583                     return v !== undefined && v !== null && v !== '' ?
10584                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10585                     };
10586                 break;
10587             case "float":
10588                 cv = function(v){
10589                     return v !== undefined && v !== null && v !== '' ?
10590                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10591                     };
10592                 break;
10593             case "bool":
10594             case "boolean":
10595                 cv = function(v){ return v === true || v === "true" || v == 1; };
10596                 break;
10597             case "date":
10598                 cv = function(v){
10599                     if(!v){
10600                         return '';
10601                     }
10602                     if(v instanceof Date){
10603                         return v;
10604                     }
10605                     if(dateFormat){
10606                         if(dateFormat == "timestamp"){
10607                             return new Date(v*1000);
10608                         }
10609                         return Date.parseDate(v, dateFormat);
10610                     }
10611                     var parsed = Date.parse(v);
10612                     return parsed ? new Date(parsed) : null;
10613                 };
10614              break;
10615             
10616         }
10617         this.convert = cv;
10618     }
10619 };
10620
10621 Roo.data.Field.prototype = {
10622     dateFormat: null,
10623     defaultValue: "",
10624     mapping: null,
10625     sortType : null,
10626     sortDir : "ASC"
10627 };/*
10628  * Based on:
10629  * Ext JS Library 1.1.1
10630  * Copyright(c) 2006-2007, Ext JS, LLC.
10631  *
10632  * Originally Released Under LGPL - original licence link has changed is not relivant.
10633  *
10634  * Fork - LGPL
10635  * <script type="text/javascript">
10636  */
10637  
10638 // Base class for reading structured data from a data source.  This class is intended to be
10639 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10640
10641 /**
10642  * @class Roo.data.DataReader
10643  * Base class for reading structured data from a data source.  This class is intended to be
10644  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10645  */
10646
10647 Roo.data.DataReader = function(meta, recordType){
10648     
10649     this.meta = meta;
10650     
10651     this.recordType = recordType instanceof Array ? 
10652         Roo.data.Record.create(recordType) : recordType;
10653 };
10654
10655 Roo.data.DataReader.prototype = {
10656      /**
10657      * Create an empty record
10658      * @param {Object} data (optional) - overlay some values
10659      * @return {Roo.data.Record} record created.
10660      */
10661     newRow :  function(d) {
10662         var da =  {};
10663         this.recordType.prototype.fields.each(function(c) {
10664             switch( c.type) {
10665                 case 'int' : da[c.name] = 0; break;
10666                 case 'date' : da[c.name] = new Date(); break;
10667                 case 'float' : da[c.name] = 0.0; break;
10668                 case 'boolean' : da[c.name] = false; break;
10669                 default : da[c.name] = ""; break;
10670             }
10671             
10672         });
10673         return new this.recordType(Roo.apply(da, d));
10674     }
10675     
10676 };/*
10677  * Based on:
10678  * Ext JS Library 1.1.1
10679  * Copyright(c) 2006-2007, Ext JS, LLC.
10680  *
10681  * Originally Released Under LGPL - original licence link has changed is not relivant.
10682  *
10683  * Fork - LGPL
10684  * <script type="text/javascript">
10685  */
10686
10687 /**
10688  * @class Roo.data.DataProxy
10689  * @extends Roo.data.Observable
10690  * This class is an abstract base class for implementations which provide retrieval of
10691  * unformatted data objects.<br>
10692  * <p>
10693  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10694  * (of the appropriate type which knows how to parse the data object) to provide a block of
10695  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10696  * <p>
10697  * Custom implementations must implement the load method as described in
10698  * {@link Roo.data.HttpProxy#load}.
10699  */
10700 Roo.data.DataProxy = function(){
10701     this.addEvents({
10702         /**
10703          * @event beforeload
10704          * Fires before a network request is made to retrieve a data object.
10705          * @param {Object} This DataProxy object.
10706          * @param {Object} params The params parameter to the load function.
10707          */
10708         beforeload : true,
10709         /**
10710          * @event load
10711          * Fires before the load method's callback is called.
10712          * @param {Object} This DataProxy object.
10713          * @param {Object} o The data object.
10714          * @param {Object} arg The callback argument object passed to the load function.
10715          */
10716         load : true,
10717         /**
10718          * @event loadexception
10719          * Fires if an Exception occurs during data retrieval.
10720          * @param {Object} This DataProxy object.
10721          * @param {Object} o The data object.
10722          * @param {Object} arg The callback argument object passed to the load function.
10723          * @param {Object} e The Exception.
10724          */
10725         loadexception : true
10726     });
10727     Roo.data.DataProxy.superclass.constructor.call(this);
10728 };
10729
10730 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10731
10732     /**
10733      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10734      */
10735 /*
10736  * Based on:
10737  * Ext JS Library 1.1.1
10738  * Copyright(c) 2006-2007, Ext JS, LLC.
10739  *
10740  * Originally Released Under LGPL - original licence link has changed is not relivant.
10741  *
10742  * Fork - LGPL
10743  * <script type="text/javascript">
10744  */
10745 /**
10746  * @class Roo.data.MemoryProxy
10747  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10748  * to the Reader when its load method is called.
10749  * @constructor
10750  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10751  */
10752 Roo.data.MemoryProxy = function(data){
10753     if (data.data) {
10754         data = data.data;
10755     }
10756     Roo.data.MemoryProxy.superclass.constructor.call(this);
10757     this.data = data;
10758 };
10759
10760 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10761     /**
10762      * Load data from the requested source (in this case an in-memory
10763      * data object passed to the constructor), read the data object into
10764      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10765      * process that block using the passed callback.
10766      * @param {Object} params This parameter is not used by the MemoryProxy class.
10767      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10768      * object into a block of Roo.data.Records.
10769      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10770      * The function must be passed <ul>
10771      * <li>The Record block object</li>
10772      * <li>The "arg" argument from the load function</li>
10773      * <li>A boolean success indicator</li>
10774      * </ul>
10775      * @param {Object} scope The scope in which to call the callback
10776      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10777      */
10778     load : function(params, reader, callback, scope, arg){
10779         params = params || {};
10780         var result;
10781         try {
10782             result = reader.readRecords(this.data);
10783         }catch(e){
10784             this.fireEvent("loadexception", this, arg, null, e);
10785             callback.call(scope, null, arg, false);
10786             return;
10787         }
10788         callback.call(scope, result, arg, true);
10789     },
10790     
10791     // private
10792     update : function(params, records){
10793         
10794     }
10795 });/*
10796  * Based on:
10797  * Ext JS Library 1.1.1
10798  * Copyright(c) 2006-2007, Ext JS, LLC.
10799  *
10800  * Originally Released Under LGPL - original licence link has changed is not relivant.
10801  *
10802  * Fork - LGPL
10803  * <script type="text/javascript">
10804  */
10805 /**
10806  * @class Roo.data.HttpProxy
10807  * @extends Roo.data.DataProxy
10808  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10809  * configured to reference a certain URL.<br><br>
10810  * <p>
10811  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10812  * from which the running page was served.<br><br>
10813  * <p>
10814  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10815  * <p>
10816  * Be aware that to enable the browser to parse an XML document, the server must set
10817  * the Content-Type header in the HTTP response to "text/xml".
10818  * @constructor
10819  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10820  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10821  * will be used to make the request.
10822  */
10823 Roo.data.HttpProxy = function(conn){
10824     Roo.data.HttpProxy.superclass.constructor.call(this);
10825     // is conn a conn config or a real conn?
10826     this.conn = conn;
10827     this.useAjax = !conn || !conn.events;
10828   
10829 };
10830
10831 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10832     // thse are take from connection...
10833     
10834     /**
10835      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10836      */
10837     /**
10838      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10839      * extra parameters to each request made by this object. (defaults to undefined)
10840      */
10841     /**
10842      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10843      *  to each request made by this object. (defaults to undefined)
10844      */
10845     /**
10846      * @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)
10847      */
10848     /**
10849      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10850      */
10851      /**
10852      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10853      * @type Boolean
10854      */
10855   
10856
10857     /**
10858      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10859      * @type Boolean
10860      */
10861     /**
10862      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10863      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10864      * a finer-grained basis than the DataProxy events.
10865      */
10866     getConnection : function(){
10867         return this.useAjax ? Roo.Ajax : this.conn;
10868     },
10869
10870     /**
10871      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10872      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10873      * process that block using the passed callback.
10874      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10875      * for the request to the remote server.
10876      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10877      * object into a block of Roo.data.Records.
10878      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10879      * The function must be passed <ul>
10880      * <li>The Record block object</li>
10881      * <li>The "arg" argument from the load function</li>
10882      * <li>A boolean success indicator</li>
10883      * </ul>
10884      * @param {Object} scope The scope in which to call the callback
10885      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10886      */
10887     load : function(params, reader, callback, scope, arg){
10888         if(this.fireEvent("beforeload", this, params) !== false){
10889             var  o = {
10890                 params : params || {},
10891                 request: {
10892                     callback : callback,
10893                     scope : scope,
10894                     arg : arg
10895                 },
10896                 reader: reader,
10897                 callback : this.loadResponse,
10898                 scope: this
10899             };
10900             if(this.useAjax){
10901                 Roo.applyIf(o, this.conn);
10902                 if(this.activeRequest){
10903                     Roo.Ajax.abort(this.activeRequest);
10904                 }
10905                 this.activeRequest = Roo.Ajax.request(o);
10906             }else{
10907                 this.conn.request(o);
10908             }
10909         }else{
10910             callback.call(scope||this, null, arg, false);
10911         }
10912     },
10913
10914     // private
10915     loadResponse : function(o, success, response){
10916         delete this.activeRequest;
10917         if(!success){
10918             this.fireEvent("loadexception", this, o, response);
10919             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10920             return;
10921         }
10922         var result;
10923         try {
10924             result = o.reader.read(response);
10925         }catch(e){
10926             this.fireEvent("loadexception", this, o, response, e);
10927             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10928             return;
10929         }
10930         
10931         this.fireEvent("load", this, o, o.request.arg);
10932         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10933     },
10934
10935     // private
10936     update : function(dataSet){
10937
10938     },
10939
10940     // private
10941     updateResponse : function(dataSet){
10942
10943     }
10944 });/*
10945  * Based on:
10946  * Ext JS Library 1.1.1
10947  * Copyright(c) 2006-2007, Ext JS, LLC.
10948  *
10949  * Originally Released Under LGPL - original licence link has changed is not relivant.
10950  *
10951  * Fork - LGPL
10952  * <script type="text/javascript">
10953  */
10954
10955 /**
10956  * @class Roo.data.ScriptTagProxy
10957  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10958  * other than the originating domain of the running page.<br><br>
10959  * <p>
10960  * <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
10961  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10962  * <p>
10963  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10964  * source code that is used as the source inside a &lt;script> tag.<br><br>
10965  * <p>
10966  * In order for the browser to process the returned data, the server must wrap the data object
10967  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10968  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10969  * depending on whether the callback name was passed:
10970  * <p>
10971  * <pre><code>
10972 boolean scriptTag = false;
10973 String cb = request.getParameter("callback");
10974 if (cb != null) {
10975     scriptTag = true;
10976     response.setContentType("text/javascript");
10977 } else {
10978     response.setContentType("application/x-json");
10979 }
10980 Writer out = response.getWriter();
10981 if (scriptTag) {
10982     out.write(cb + "(");
10983 }
10984 out.print(dataBlock.toJsonString());
10985 if (scriptTag) {
10986     out.write(");");
10987 }
10988 </pre></code>
10989  *
10990  * @constructor
10991  * @param {Object} config A configuration object.
10992  */
10993 Roo.data.ScriptTagProxy = function(config){
10994     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10995     Roo.apply(this, config);
10996     this.head = document.getElementsByTagName("head")[0];
10997 };
10998
10999 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11000
11001 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11002     /**
11003      * @cfg {String} url The URL from which to request the data object.
11004      */
11005     /**
11006      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11007      */
11008     timeout : 30000,
11009     /**
11010      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11011      * the server the name of the callback function set up by the load call to process the returned data object.
11012      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11013      * javascript output which calls this named function passing the data object as its only parameter.
11014      */
11015     callbackParam : "callback",
11016     /**
11017      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11018      * name to the request.
11019      */
11020     nocache : true,
11021
11022     /**
11023      * Load data from the configured URL, read the data object into
11024      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11025      * process that block using the passed callback.
11026      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11027      * for the request to the remote server.
11028      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11029      * object into a block of Roo.data.Records.
11030      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11031      * The function must be passed <ul>
11032      * <li>The Record block object</li>
11033      * <li>The "arg" argument from the load function</li>
11034      * <li>A boolean success indicator</li>
11035      * </ul>
11036      * @param {Object} scope The scope in which to call the callback
11037      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11038      */
11039     load : function(params, reader, callback, scope, arg){
11040         if(this.fireEvent("beforeload", this, params) !== false){
11041
11042             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11043
11044             var url = this.url;
11045             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11046             if(this.nocache){
11047                 url += "&_dc=" + (new Date().getTime());
11048             }
11049             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11050             var trans = {
11051                 id : transId,
11052                 cb : "stcCallback"+transId,
11053                 scriptId : "stcScript"+transId,
11054                 params : params,
11055                 arg : arg,
11056                 url : url,
11057                 callback : callback,
11058                 scope : scope,
11059                 reader : reader
11060             };
11061             var conn = this;
11062
11063             window[trans.cb] = function(o){
11064                 conn.handleResponse(o, trans);
11065             };
11066
11067             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11068
11069             if(this.autoAbort !== false){
11070                 this.abort();
11071             }
11072
11073             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11074
11075             var script = document.createElement("script");
11076             script.setAttribute("src", url);
11077             script.setAttribute("type", "text/javascript");
11078             script.setAttribute("id", trans.scriptId);
11079             this.head.appendChild(script);
11080
11081             this.trans = trans;
11082         }else{
11083             callback.call(scope||this, null, arg, false);
11084         }
11085     },
11086
11087     // private
11088     isLoading : function(){
11089         return this.trans ? true : false;
11090     },
11091
11092     /**
11093      * Abort the current server request.
11094      */
11095     abort : function(){
11096         if(this.isLoading()){
11097             this.destroyTrans(this.trans);
11098         }
11099     },
11100
11101     // private
11102     destroyTrans : function(trans, isLoaded){
11103         this.head.removeChild(document.getElementById(trans.scriptId));
11104         clearTimeout(trans.timeoutId);
11105         if(isLoaded){
11106             window[trans.cb] = undefined;
11107             try{
11108                 delete window[trans.cb];
11109             }catch(e){}
11110         }else{
11111             // if hasn't been loaded, wait for load to remove it to prevent script error
11112             window[trans.cb] = function(){
11113                 window[trans.cb] = undefined;
11114                 try{
11115                     delete window[trans.cb];
11116                 }catch(e){}
11117             };
11118         }
11119     },
11120
11121     // private
11122     handleResponse : function(o, trans){
11123         this.trans = false;
11124         this.destroyTrans(trans, true);
11125         var result;
11126         try {
11127             result = trans.reader.readRecords(o);
11128         }catch(e){
11129             this.fireEvent("loadexception", this, o, trans.arg, e);
11130             trans.callback.call(trans.scope||window, null, trans.arg, false);
11131             return;
11132         }
11133         this.fireEvent("load", this, o, trans.arg);
11134         trans.callback.call(trans.scope||window, result, trans.arg, true);
11135     },
11136
11137     // private
11138     handleFailure : function(trans){
11139         this.trans = false;
11140         this.destroyTrans(trans, false);
11141         this.fireEvent("loadexception", this, null, trans.arg);
11142         trans.callback.call(trans.scope||window, null, trans.arg, false);
11143     }
11144 });/*
11145  * Based on:
11146  * Ext JS Library 1.1.1
11147  * Copyright(c) 2006-2007, Ext JS, LLC.
11148  *
11149  * Originally Released Under LGPL - original licence link has changed is not relivant.
11150  *
11151  * Fork - LGPL
11152  * <script type="text/javascript">
11153  */
11154
11155 /**
11156  * @class Roo.data.JsonReader
11157  * @extends Roo.data.DataReader
11158  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11159  * based on mappings in a provided Roo.data.Record constructor.
11160  * 
11161  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11162  * in the reply previously. 
11163  * 
11164  * <p>
11165  * Example code:
11166  * <pre><code>
11167 var RecordDef = Roo.data.Record.create([
11168     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11169     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11170 ]);
11171 var myReader = new Roo.data.JsonReader({
11172     totalProperty: "results",    // The property which contains the total dataset size (optional)
11173     root: "rows",                // The property which contains an Array of row objects
11174     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11175 }, RecordDef);
11176 </code></pre>
11177  * <p>
11178  * This would consume a JSON file like this:
11179  * <pre><code>
11180 { 'results': 2, 'rows': [
11181     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11182     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11183 }
11184 </code></pre>
11185  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11186  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11187  * paged from the remote server.
11188  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11189  * @cfg {String} root name of the property which contains the Array of row objects.
11190  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11191  * @cfg {Array} fields Array of field definition objects
11192  * @constructor
11193  * Create a new JsonReader
11194  * @param {Object} meta Metadata configuration options
11195  * @param {Object} recordType Either an Array of field definition objects,
11196  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11197  */
11198 Roo.data.JsonReader = function(meta, recordType){
11199     
11200     meta = meta || {};
11201     // set some defaults:
11202     Roo.applyIf(meta, {
11203         totalProperty: 'total',
11204         successProperty : 'success',
11205         root : 'data',
11206         id : 'id'
11207     });
11208     
11209     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11210 };
11211 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11212     
11213     /**
11214      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11215      * Used by Store query builder to append _requestMeta to params.
11216      * 
11217      */
11218     metaFromRemote : false,
11219     /**
11220      * This method is only used by a DataProxy which has retrieved data from a remote server.
11221      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11222      * @return {Object} data A data block which is used by an Roo.data.Store object as
11223      * a cache of Roo.data.Records.
11224      */
11225     read : function(response){
11226         var json = response.responseText;
11227        
11228         var o = /* eval:var:o */ eval("("+json+")");
11229         if(!o) {
11230             throw {message: "JsonReader.read: Json object not found"};
11231         }
11232         
11233         if(o.metaData){
11234             
11235             delete this.ef;
11236             this.metaFromRemote = true;
11237             this.meta = o.metaData;
11238             this.recordType = Roo.data.Record.create(o.metaData.fields);
11239             this.onMetaChange(this.meta, this.recordType, o);
11240         }
11241         return this.readRecords(o);
11242     },
11243
11244     // private function a store will implement
11245     onMetaChange : function(meta, recordType, o){
11246
11247     },
11248
11249     /**
11250          * @ignore
11251          */
11252     simpleAccess: function(obj, subsc) {
11253         return obj[subsc];
11254     },
11255
11256         /**
11257          * @ignore
11258          */
11259     getJsonAccessor: function(){
11260         var re = /[\[\.]/;
11261         return function(expr) {
11262             try {
11263                 return(re.test(expr))
11264                     ? new Function("obj", "return obj." + expr)
11265                     : function(obj){
11266                         return obj[expr];
11267                     };
11268             } catch(e){}
11269             return Roo.emptyFn;
11270         };
11271     }(),
11272
11273     /**
11274      * Create a data block containing Roo.data.Records from an XML document.
11275      * @param {Object} o An object which contains an Array of row objects in the property specified
11276      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11277      * which contains the total size of the dataset.
11278      * @return {Object} data A data block which is used by an Roo.data.Store object as
11279      * a cache of Roo.data.Records.
11280      */
11281     readRecords : function(o){
11282         /**
11283          * After any data loads, the raw JSON data is available for further custom processing.
11284          * @type Object
11285          */
11286         this.o = o;
11287         var s = this.meta, Record = this.recordType,
11288             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11289
11290 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11291         if (!this.ef) {
11292             if(s.totalProperty) {
11293                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11294                 }
11295                 if(s.successProperty) {
11296                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11297                 }
11298                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11299                 if (s.id) {
11300                         var g = this.getJsonAccessor(s.id);
11301                         this.getId = function(rec) {
11302                                 var r = g(rec);  
11303                                 return (r === undefined || r === "") ? null : r;
11304                         };
11305                 } else {
11306                         this.getId = function(){return null;};
11307                 }
11308             this.ef = [];
11309             for(var jj = 0; jj < fl; jj++){
11310                 f = fi[jj];
11311                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11312                 this.ef[jj] = this.getJsonAccessor(map);
11313             }
11314         }
11315
11316         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11317         if(s.totalProperty){
11318             var vt = parseInt(this.getTotal(o), 10);
11319             if(!isNaN(vt)){
11320                 totalRecords = vt;
11321             }
11322         }
11323         if(s.successProperty){
11324             var vs = this.getSuccess(o);
11325             if(vs === false || vs === 'false'){
11326                 success = false;
11327             }
11328         }
11329         var records = [];
11330         for(var i = 0; i < c; i++){
11331                 var n = root[i];
11332             var values = {};
11333             var id = this.getId(n);
11334             for(var j = 0; j < fl; j++){
11335                 f = fi[j];
11336             var v = this.ef[j](n);
11337             if (!f.convert) {
11338                 Roo.log('missing convert for ' + f.name);
11339                 Roo.log(f);
11340                 continue;
11341             }
11342             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11343             }
11344             var record = new Record(values, id);
11345             record.json = n;
11346             records[i] = record;
11347         }
11348         return {
11349             raw : o,
11350             success : success,
11351             records : records,
11352             totalRecords : totalRecords
11353         };
11354     }
11355 });/*
11356  * Based on:
11357  * Ext JS Library 1.1.1
11358  * Copyright(c) 2006-2007, Ext JS, LLC.
11359  *
11360  * Originally Released Under LGPL - original licence link has changed is not relivant.
11361  *
11362  * Fork - LGPL
11363  * <script type="text/javascript">
11364  */
11365
11366 /**
11367  * @class Roo.data.ArrayReader
11368  * @extends Roo.data.DataReader
11369  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11370  * Each element of that Array represents a row of data fields. The
11371  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11372  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11373  * <p>
11374  * Example code:.
11375  * <pre><code>
11376 var RecordDef = Roo.data.Record.create([
11377     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11378     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11379 ]);
11380 var myReader = new Roo.data.ArrayReader({
11381     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11382 }, RecordDef);
11383 </code></pre>
11384  * <p>
11385  * This would consume an Array like this:
11386  * <pre><code>
11387 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11388   </code></pre>
11389  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11390  * @constructor
11391  * Create a new JsonReader
11392  * @param {Object} meta Metadata configuration options.
11393  * @param {Object} recordType Either an Array of field definition objects
11394  * as specified to {@link Roo.data.Record#create},
11395  * or an {@link Roo.data.Record} object
11396  * created using {@link Roo.data.Record#create}.
11397  */
11398 Roo.data.ArrayReader = function(meta, recordType){
11399     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11400 };
11401
11402 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11403     /**
11404      * Create a data block containing Roo.data.Records from an XML document.
11405      * @param {Object} o An Array of row objects which represents the dataset.
11406      * @return {Object} data A data block which is used by an Roo.data.Store object as
11407      * a cache of Roo.data.Records.
11408      */
11409     readRecords : function(o){
11410         var sid = this.meta ? this.meta.id : null;
11411         var recordType = this.recordType, fields = recordType.prototype.fields;
11412         var records = [];
11413         var root = o;
11414             for(var i = 0; i < root.length; i++){
11415                     var n = root[i];
11416                 var values = {};
11417                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11418                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11419                 var f = fields.items[j];
11420                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11421                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11422                 v = f.convert(v);
11423                 values[f.name] = v;
11424             }
11425                 var record = new recordType(values, id);
11426                 record.json = n;
11427                 records[records.length] = record;
11428             }
11429             return {
11430                 records : records,
11431                 totalRecords : records.length
11432             };
11433     }
11434 });/*
11435  * - LGPL
11436  * * 
11437  */
11438
11439 /**
11440  * @class Roo.bootstrap.ComboBox
11441  * @extends Roo.bootstrap.TriggerField
11442  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11443  * @cfg {Boolean} append (true|false) default false
11444  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11445  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11446  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11447  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11448  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11449  * @cfg {Boolean} animate default true
11450  * @cfg {Boolean} emptyResultText only for touch device
11451  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11452  * @constructor
11453  * Create a new ComboBox.
11454  * @param {Object} config Configuration options
11455  */
11456 Roo.bootstrap.ComboBox = function(config){
11457     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11458     this.addEvents({
11459         /**
11460          * @event expand
11461          * Fires when the dropdown list is expanded
11462              * @param {Roo.bootstrap.ComboBox} combo This combo box
11463              */
11464         'expand' : true,
11465         /**
11466          * @event collapse
11467          * Fires when the dropdown list is collapsed
11468              * @param {Roo.bootstrap.ComboBox} combo This combo box
11469              */
11470         'collapse' : true,
11471         /**
11472          * @event beforeselect
11473          * Fires before a list item is selected. Return false to cancel the selection.
11474              * @param {Roo.bootstrap.ComboBox} combo This combo box
11475              * @param {Roo.data.Record} record The data record returned from the underlying store
11476              * @param {Number} index The index of the selected item in the dropdown list
11477              */
11478         'beforeselect' : true,
11479         /**
11480          * @event select
11481          * Fires when a list item is selected
11482              * @param {Roo.bootstrap.ComboBox} combo This combo box
11483              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11484              * @param {Number} index The index of the selected item in the dropdown list
11485              */
11486         'select' : true,
11487         /**
11488          * @event beforequery
11489          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11490          * The event object passed has these properties:
11491              * @param {Roo.bootstrap.ComboBox} combo This combo box
11492              * @param {String} query The query
11493              * @param {Boolean} forceAll true to force "all" query
11494              * @param {Boolean} cancel true to cancel the query
11495              * @param {Object} e The query event object
11496              */
11497         'beforequery': true,
11498          /**
11499          * @event add
11500          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11501              * @param {Roo.bootstrap.ComboBox} combo This combo box
11502              */
11503         'add' : true,
11504         /**
11505          * @event edit
11506          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11507              * @param {Roo.bootstrap.ComboBox} combo This combo box
11508              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11509              */
11510         'edit' : true,
11511         /**
11512          * @event remove
11513          * Fires when the remove value from the combobox array
11514              * @param {Roo.bootstrap.ComboBox} combo This combo box
11515              */
11516         'remove' : true,
11517         /**
11518          * @event specialfilter
11519          * Fires when specialfilter
11520             * @param {Roo.bootstrap.ComboBox} combo This combo box
11521             */
11522         'specialfilter' : true,
11523         /**
11524          * @event tick
11525          * Fires when tick the element
11526             * @param {Roo.bootstrap.ComboBox} combo This combo box
11527             */
11528         'tick' : true,
11529         /**
11530          * @event touchviewdisplay
11531          * Fires when touch view require special display (default is using displayField)
11532             * @param {Roo.bootstrap.ComboBox} combo This combo box
11533             * @param {Object} cfg set html .
11534             */
11535         'touchviewdisplay' : true
11536         
11537     });
11538     
11539     this.item = [];
11540     this.tickItems = [];
11541     
11542     this.selectedIndex = -1;
11543     if(this.mode == 'local'){
11544         if(config.queryDelay === undefined){
11545             this.queryDelay = 10;
11546         }
11547         if(config.minChars === undefined){
11548             this.minChars = 0;
11549         }
11550     }
11551 };
11552
11553 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11554      
11555     /**
11556      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11557      * rendering into an Roo.Editor, defaults to false)
11558      */
11559     /**
11560      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11561      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11562      */
11563     /**
11564      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11565      */
11566     /**
11567      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11568      * the dropdown list (defaults to undefined, with no header element)
11569      */
11570
11571      /**
11572      * @cfg {String/Roo.Template} tpl The template to use to render the output
11573      */
11574      
11575      /**
11576      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11577      */
11578     listWidth: undefined,
11579     /**
11580      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11581      * mode = 'remote' or 'text' if mode = 'local')
11582      */
11583     displayField: undefined,
11584     
11585     /**
11586      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11587      * mode = 'remote' or 'value' if mode = 'local'). 
11588      * Note: use of a valueField requires the user make a selection
11589      * in order for a value to be mapped.
11590      */
11591     valueField: undefined,
11592     
11593     
11594     /**
11595      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11596      * field's data value (defaults to the underlying DOM element's name)
11597      */
11598     hiddenName: undefined,
11599     /**
11600      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11601      */
11602     listClass: '',
11603     /**
11604      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11605      */
11606     selectedClass: 'active',
11607     
11608     /**
11609      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11610      */
11611     shadow:'sides',
11612     /**
11613      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11614      * anchor positions (defaults to 'tl-bl')
11615      */
11616     listAlign: 'tl-bl?',
11617     /**
11618      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11619      */
11620     maxHeight: 300,
11621     /**
11622      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11623      * query specified by the allQuery config option (defaults to 'query')
11624      */
11625     triggerAction: 'query',
11626     /**
11627      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11628      * (defaults to 4, does not apply if editable = false)
11629      */
11630     minChars : 4,
11631     /**
11632      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11633      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11634      */
11635     typeAhead: false,
11636     /**
11637      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11638      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11639      */
11640     queryDelay: 500,
11641     /**
11642      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11643      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11644      */
11645     pageSize: 0,
11646     /**
11647      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11648      * when editable = true (defaults to false)
11649      */
11650     selectOnFocus:false,
11651     /**
11652      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11653      */
11654     queryParam: 'query',
11655     /**
11656      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11657      * when mode = 'remote' (defaults to 'Loading...')
11658      */
11659     loadingText: 'Loading...',
11660     /**
11661      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11662      */
11663     resizable: false,
11664     /**
11665      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11666      */
11667     handleHeight : 8,
11668     /**
11669      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11670      * traditional select (defaults to true)
11671      */
11672     editable: true,
11673     /**
11674      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11675      */
11676     allQuery: '',
11677     /**
11678      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11679      */
11680     mode: 'remote',
11681     /**
11682      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11683      * listWidth has a higher value)
11684      */
11685     minListWidth : 70,
11686     /**
11687      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11688      * allow the user to set arbitrary text into the field (defaults to false)
11689      */
11690     forceSelection:false,
11691     /**
11692      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11693      * if typeAhead = true (defaults to 250)
11694      */
11695     typeAheadDelay : 250,
11696     /**
11697      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11698      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11699      */
11700     valueNotFoundText : undefined,
11701     /**
11702      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11703      */
11704     blockFocus : false,
11705     
11706     /**
11707      * @cfg {Boolean} disableClear Disable showing of clear button.
11708      */
11709     disableClear : false,
11710     /**
11711      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11712      */
11713     alwaysQuery : false,
11714     
11715     /**
11716      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11717      */
11718     multiple : false,
11719     
11720     /**
11721      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11722      */
11723     invalidClass : "has-warning",
11724     
11725     /**
11726      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11727      */
11728     validClass : "has-success",
11729     
11730     /**
11731      * @cfg {Boolean} specialFilter (true|false) special filter default false
11732      */
11733     specialFilter : false,
11734     
11735     /**
11736      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11737      */
11738     mobileTouchView : true,
11739     
11740     //private
11741     addicon : false,
11742     editicon: false,
11743     
11744     page: 0,
11745     hasQuery: false,
11746     append: false,
11747     loadNext: false,
11748     autoFocus : true,
11749     tickable : false,
11750     btnPosition : 'right',
11751     triggerList : true,
11752     showToggleBtn : true,
11753     animate : true,
11754     emptyResultText: 'Empty',
11755     triggerText : 'Select',
11756     
11757     // element that contains real text value.. (when hidden is used..)
11758     
11759     getAutoCreate : function()
11760     {
11761         var cfg = false;
11762         
11763         /*
11764          * Touch Devices
11765          */
11766         
11767         if(Roo.isTouch && this.mobileTouchView){
11768             cfg = this.getAutoCreateTouchView();
11769             return cfg;;
11770         }
11771         
11772         /*
11773          *  Normal ComboBox
11774          */
11775         if(!this.tickable){
11776             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11777             return cfg;
11778         }
11779         
11780         /*
11781          *  ComboBox with tickable selections
11782          */
11783              
11784         var align = this.labelAlign || this.parentLabelAlign();
11785         
11786         cfg = {
11787             cls : 'form-group roo-combobox-tickable' //input-group
11788         };
11789         
11790         var buttons = {
11791             tag : 'div',
11792             cls : 'tickable-buttons',
11793             cn : [
11794                 {
11795                     tag : 'button',
11796                     type : 'button',
11797                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11798                     html : this.triggerText
11799                 },
11800                 {
11801                     tag : 'button',
11802                     type : 'button',
11803                     name : 'ok',
11804                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11805                     html : 'Done'
11806                 },
11807                 {
11808                     tag : 'button',
11809                     type : 'button',
11810                     name : 'cancel',
11811                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11812                     html : 'Cancel'
11813                 }
11814             ]
11815         };
11816         
11817         if(this.editable){
11818             buttons.cn.unshift({
11819                 tag: 'input',
11820                 cls: 'select2-search-field-input'
11821             });
11822         }
11823         
11824         var _this = this;
11825         
11826         Roo.each(buttons.cn, function(c){
11827             if (_this.size) {
11828                 c.cls += ' btn-' + _this.size;
11829             }
11830
11831             if (_this.disabled) {
11832                 c.disabled = true;
11833             }
11834         });
11835         
11836         var box = {
11837             tag: 'div',
11838             cn: [
11839                 {
11840                     tag: 'input',
11841                     type : 'hidden',
11842                     cls: 'form-hidden-field'
11843                 },
11844                 {
11845                     tag: 'ul',
11846                     cls: 'select2-choices',
11847                     cn:[
11848                         {
11849                             tag: 'li',
11850                             cls: 'select2-search-field',
11851                             cn: [
11852
11853                                 buttons
11854                             ]
11855                         }
11856                     ]
11857                 }
11858             ]
11859         };
11860         
11861         var combobox = {
11862             cls: 'select2-container input-group select2-container-multi',
11863             cn: [
11864                 box
11865 //                {
11866 //                    tag: 'ul',
11867 //                    cls: 'typeahead typeahead-long dropdown-menu',
11868 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11869 //                }
11870             ]
11871         };
11872         
11873         if(this.hasFeedback && !this.allowBlank){
11874             
11875             var feedback = {
11876                 tag: 'span',
11877                 cls: 'glyphicon form-control-feedback'
11878             };
11879
11880             combobox.cn.push(feedback);
11881         }
11882         
11883         if (align ==='left' && this.fieldLabel.length) {
11884             
11885                 Roo.log("left and has label");
11886                 cfg.cn = [
11887                     
11888                     {
11889                         tag: 'label',
11890                         'for' :  id,
11891                         cls : 'control-label col-sm-' + this.labelWidth,
11892                         html : this.fieldLabel
11893                         
11894                     },
11895                     {
11896                         cls : "col-sm-" + (12 - this.labelWidth), 
11897                         cn: [
11898                             combobox
11899                         ]
11900                     }
11901                     
11902                 ];
11903         } else if ( this.fieldLabel.length) {
11904                 Roo.log(" label");
11905                  cfg.cn = [
11906                    
11907                     {
11908                         tag: 'label',
11909                         //cls : 'input-group-addon',
11910                         html : this.fieldLabel
11911                         
11912                     },
11913                     
11914                     combobox
11915                     
11916                 ];
11917
11918         } else {
11919             
11920                 Roo.log(" no label && no align");
11921                 cfg = combobox
11922                      
11923                 
11924         }
11925          
11926         var settings=this;
11927         ['xs','sm','md','lg'].map(function(size){
11928             if (settings[size]) {
11929                 cfg.cls += ' col-' + size + '-' + settings[size];
11930             }
11931         });
11932         
11933         return cfg;
11934         
11935     },
11936     
11937     _initEventsCalled : false,
11938     
11939     // private
11940     initEvents: function()
11941     {
11942         
11943         if (this._initEventsCalled) { // as we call render... prevent looping...
11944             return;
11945         }
11946         this._initEventsCalled = true;
11947         
11948         if (!this.store) {
11949             throw "can not find store for combo";
11950         }
11951         
11952         this.store = Roo.factory(this.store, Roo.data);
11953         
11954         // if we are building from html. then this element is so complex, that we can not really
11955         // use the rendered HTML.
11956         // so we have to trash and replace the previous code.
11957         if (Roo.XComponent.build_from_html) {
11958             
11959             // remove this element....
11960             var e = this.el.dom, k=0;
11961             while (e ) { e = e.previousSibling;  ++k;}
11962
11963             this.el.remove();
11964             
11965             this.el=false;
11966             this.rendered = false;
11967             
11968             this.render(this.parent().getChildContainer(true), k);
11969             
11970             
11971             
11972         }
11973         
11974         
11975         /*
11976          * Touch Devices
11977          */
11978         
11979         if(Roo.isTouch && this.mobileTouchView){
11980             this.initTouchView();
11981             return;
11982         }
11983         
11984         if(this.tickable){
11985             this.initTickableEvents();
11986             return;
11987         }
11988         
11989         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11990         
11991         if(this.hiddenName){
11992             
11993             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11994             
11995             this.hiddenField.dom.value =
11996                 this.hiddenValue !== undefined ? this.hiddenValue :
11997                 this.value !== undefined ? this.value : '';
11998
11999             // prevent input submission
12000             this.el.dom.removeAttribute('name');
12001             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12002              
12003              
12004         }
12005         //if(Roo.isGecko){
12006         //    this.el.dom.setAttribute('autocomplete', 'off');
12007         //}
12008         
12009         var cls = 'x-combo-list';
12010         
12011         //this.list = new Roo.Layer({
12012         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12013         //});
12014         
12015         var _this = this;
12016         
12017         (function(){
12018             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12019             _this.list.setWidth(lw);
12020         }).defer(100);
12021         
12022         this.list.on('mouseover', this.onViewOver, this);
12023         this.list.on('mousemove', this.onViewMove, this);
12024         
12025         this.list.on('scroll', this.onViewScroll, this);
12026         
12027         /*
12028         this.list.swallowEvent('mousewheel');
12029         this.assetHeight = 0;
12030
12031         if(this.title){
12032             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12033             this.assetHeight += this.header.getHeight();
12034         }
12035
12036         this.innerList = this.list.createChild({cls:cls+'-inner'});
12037         this.innerList.on('mouseover', this.onViewOver, this);
12038         this.innerList.on('mousemove', this.onViewMove, this);
12039         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12040         
12041         if(this.allowBlank && !this.pageSize && !this.disableClear){
12042             this.footer = this.list.createChild({cls:cls+'-ft'});
12043             this.pageTb = new Roo.Toolbar(this.footer);
12044            
12045         }
12046         if(this.pageSize){
12047             this.footer = this.list.createChild({cls:cls+'-ft'});
12048             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12049                     {pageSize: this.pageSize});
12050             
12051         }
12052         
12053         if (this.pageTb && this.allowBlank && !this.disableClear) {
12054             var _this = this;
12055             this.pageTb.add(new Roo.Toolbar.Fill(), {
12056                 cls: 'x-btn-icon x-btn-clear',
12057                 text: '&#160;',
12058                 handler: function()
12059                 {
12060                     _this.collapse();
12061                     _this.clearValue();
12062                     _this.onSelect(false, -1);
12063                 }
12064             });
12065         }
12066         if (this.footer) {
12067             this.assetHeight += this.footer.getHeight();
12068         }
12069         */
12070             
12071         if(!this.tpl){
12072             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12073         }
12074
12075         this.view = new Roo.View(this.list, this.tpl, {
12076             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12077         });
12078         //this.view.wrapEl.setDisplayed(false);
12079         this.view.on('click', this.onViewClick, this);
12080         
12081         
12082         
12083         this.store.on('beforeload', this.onBeforeLoad, this);
12084         this.store.on('load', this.onLoad, this);
12085         this.store.on('loadexception', this.onLoadException, this);
12086         /*
12087         if(this.resizable){
12088             this.resizer = new Roo.Resizable(this.list,  {
12089                pinned:true, handles:'se'
12090             });
12091             this.resizer.on('resize', function(r, w, h){
12092                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12093                 this.listWidth = w;
12094                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12095                 this.restrictHeight();
12096             }, this);
12097             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12098         }
12099         */
12100         if(!this.editable){
12101             this.editable = true;
12102             this.setEditable(false);
12103         }
12104         
12105         /*
12106         
12107         if (typeof(this.events.add.listeners) != 'undefined') {
12108             
12109             this.addicon = this.wrap.createChild(
12110                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12111        
12112             this.addicon.on('click', function(e) {
12113                 this.fireEvent('add', this);
12114             }, this);
12115         }
12116         if (typeof(this.events.edit.listeners) != 'undefined') {
12117             
12118             this.editicon = this.wrap.createChild(
12119                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12120             if (this.addicon) {
12121                 this.editicon.setStyle('margin-left', '40px');
12122             }
12123             this.editicon.on('click', function(e) {
12124                 
12125                 // we fire even  if inothing is selected..
12126                 this.fireEvent('edit', this, this.lastData );
12127                 
12128             }, this);
12129         }
12130         */
12131         
12132         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12133             "up" : function(e){
12134                 this.inKeyMode = true;
12135                 this.selectPrev();
12136             },
12137
12138             "down" : function(e){
12139                 if(!this.isExpanded()){
12140                     this.onTriggerClick();
12141                 }else{
12142                     this.inKeyMode = true;
12143                     this.selectNext();
12144                 }
12145             },
12146
12147             "enter" : function(e){
12148 //                this.onViewClick();
12149                 //return true;
12150                 this.collapse();
12151                 
12152                 if(this.fireEvent("specialkey", this, e)){
12153                     this.onViewClick(false);
12154                 }
12155                 
12156                 return true;
12157             },
12158
12159             "esc" : function(e){
12160                 this.collapse();
12161             },
12162
12163             "tab" : function(e){
12164                 this.collapse();
12165                 
12166                 if(this.fireEvent("specialkey", this, e)){
12167                     this.onViewClick(false);
12168                 }
12169                 
12170                 return true;
12171             },
12172
12173             scope : this,
12174
12175             doRelay : function(foo, bar, hname){
12176                 if(hname == 'down' || this.scope.isExpanded()){
12177                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12178                 }
12179                 return true;
12180             },
12181
12182             forceKeyDown: true
12183         });
12184         
12185         
12186         this.queryDelay = Math.max(this.queryDelay || 10,
12187                 this.mode == 'local' ? 10 : 250);
12188         
12189         
12190         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12191         
12192         if(this.typeAhead){
12193             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12194         }
12195         if(this.editable !== false){
12196             this.inputEl().on("keyup", this.onKeyUp, this);
12197         }
12198         if(this.forceSelection){
12199             this.inputEl().on('blur', this.doForce, this);
12200         }
12201         
12202         if(this.multiple){
12203             this.choices = this.el.select('ul.select2-choices', true).first();
12204             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12205         }
12206     },
12207     
12208     initTickableEvents: function()
12209     {   
12210         this.createList();
12211         
12212         if(this.hiddenName){
12213             
12214             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12215             
12216             this.hiddenField.dom.value =
12217                 this.hiddenValue !== undefined ? this.hiddenValue :
12218                 this.value !== undefined ? this.value : '';
12219
12220             // prevent input submission
12221             this.el.dom.removeAttribute('name');
12222             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12223              
12224              
12225         }
12226         
12227 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12228         
12229         this.choices = this.el.select('ul.select2-choices', true).first();
12230         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12231         if(this.triggerList){
12232             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12233         }
12234          
12235         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12236         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12237         
12238         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12239         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12240         
12241         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12242         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12243         
12244         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12245         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12246         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12247         
12248         this.okBtn.hide();
12249         this.cancelBtn.hide();
12250         
12251         var _this = this;
12252         
12253         (function(){
12254             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12255             _this.list.setWidth(lw);
12256         }).defer(100);
12257         
12258         this.list.on('mouseover', this.onViewOver, this);
12259         this.list.on('mousemove', this.onViewMove, this);
12260         
12261         this.list.on('scroll', this.onViewScroll, this);
12262         
12263         if(!this.tpl){
12264             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>';
12265         }
12266
12267         this.view = new Roo.View(this.list, this.tpl, {
12268             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12269         });
12270         
12271         //this.view.wrapEl.setDisplayed(false);
12272         this.view.on('click', this.onViewClick, this);
12273         
12274         
12275         
12276         this.store.on('beforeload', this.onBeforeLoad, this);
12277         this.store.on('load', this.onLoad, this);
12278         this.store.on('loadexception', this.onLoadException, this);
12279         
12280         if(this.editable){
12281             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12282                 "up" : function(e){
12283                     this.inKeyMode = true;
12284                     this.selectPrev();
12285                 },
12286
12287                 "down" : function(e){
12288                     this.inKeyMode = true;
12289                     this.selectNext();
12290                 },
12291
12292                 "enter" : function(e){
12293                     if(this.fireEvent("specialkey", this, e)){
12294                         this.onViewClick(false);
12295                     }
12296                     
12297                     return true;
12298                 },
12299
12300                 "esc" : function(e){
12301                     this.onTickableFooterButtonClick(e, false, false);
12302                 },
12303
12304                 "tab" : function(e){
12305                     this.fireEvent("specialkey", this, e);
12306                     
12307                     this.onTickableFooterButtonClick(e, false, false);
12308                     
12309                     return true;
12310                 },
12311
12312                 scope : this,
12313
12314                 doRelay : function(e, fn, key){
12315                     if(this.scope.isExpanded()){
12316                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12317                     }
12318                     return true;
12319                 },
12320
12321                 forceKeyDown: true
12322             });
12323         }
12324         
12325         this.queryDelay = Math.max(this.queryDelay || 10,
12326                 this.mode == 'local' ? 10 : 250);
12327         
12328         
12329         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12330         
12331         if(this.typeAhead){
12332             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12333         }
12334         
12335         if(this.editable !== false){
12336             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12337         }
12338         
12339     },
12340
12341     onDestroy : function(){
12342         if(this.view){
12343             this.view.setStore(null);
12344             this.view.el.removeAllListeners();
12345             this.view.el.remove();
12346             this.view.purgeListeners();
12347         }
12348         if(this.list){
12349             this.list.dom.innerHTML  = '';
12350         }
12351         
12352         if(this.store){
12353             this.store.un('beforeload', this.onBeforeLoad, this);
12354             this.store.un('load', this.onLoad, this);
12355             this.store.un('loadexception', this.onLoadException, this);
12356         }
12357         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12358     },
12359
12360     // private
12361     fireKey : function(e){
12362         if(e.isNavKeyPress() && !this.list.isVisible()){
12363             this.fireEvent("specialkey", this, e);
12364         }
12365     },
12366
12367     // private
12368     onResize: function(w, h){
12369 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12370 //        
12371 //        if(typeof w != 'number'){
12372 //            // we do not handle it!?!?
12373 //            return;
12374 //        }
12375 //        var tw = this.trigger.getWidth();
12376 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12377 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12378 //        var x = w - tw;
12379 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12380 //            
12381 //        //this.trigger.setStyle('left', x+'px');
12382 //        
12383 //        if(this.list && this.listWidth === undefined){
12384 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12385 //            this.list.setWidth(lw);
12386 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12387 //        }
12388         
12389     
12390         
12391     },
12392
12393     /**
12394      * Allow or prevent the user from directly editing the field text.  If false is passed,
12395      * the user will only be able to select from the items defined in the dropdown list.  This method
12396      * is the runtime equivalent of setting the 'editable' config option at config time.
12397      * @param {Boolean} value True to allow the user to directly edit the field text
12398      */
12399     setEditable : function(value){
12400         if(value == this.editable){
12401             return;
12402         }
12403         this.editable = value;
12404         if(!value){
12405             this.inputEl().dom.setAttribute('readOnly', true);
12406             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12407             this.inputEl().addClass('x-combo-noedit');
12408         }else{
12409             this.inputEl().dom.setAttribute('readOnly', false);
12410             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12411             this.inputEl().removeClass('x-combo-noedit');
12412         }
12413     },
12414
12415     // private
12416     
12417     onBeforeLoad : function(combo,opts){
12418         if(!this.hasFocus){
12419             return;
12420         }
12421          if (!opts.add) {
12422             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12423          }
12424         this.restrictHeight();
12425         this.selectedIndex = -1;
12426     },
12427
12428     // private
12429     onLoad : function(){
12430         
12431         this.hasQuery = false;
12432         
12433         if(!this.hasFocus){
12434             return;
12435         }
12436         
12437         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12438             this.loading.hide();
12439         }
12440              
12441         if(this.store.getCount() > 0){
12442             this.expand();
12443             this.restrictHeight();
12444             if(this.lastQuery == this.allQuery){
12445                 if(this.editable && !this.tickable){
12446                     this.inputEl().dom.select();
12447                 }
12448                 
12449                 if(
12450                     !this.selectByValue(this.value, true) &&
12451                     this.autoFocus && 
12452                     (
12453                         !this.store.lastOptions ||
12454                         typeof(this.store.lastOptions.add) == 'undefined' || 
12455                         this.store.lastOptions.add != true
12456                     )
12457                 ){
12458                     this.select(0, true);
12459                 }
12460             }else{
12461                 if(this.autoFocus){
12462                     this.selectNext();
12463                 }
12464                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12465                     this.taTask.delay(this.typeAheadDelay);
12466                 }
12467             }
12468         }else{
12469             this.onEmptyResults();
12470         }
12471         
12472         //this.el.focus();
12473     },
12474     // private
12475     onLoadException : function()
12476     {
12477         this.hasQuery = false;
12478         
12479         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12480             this.loading.hide();
12481         }
12482         
12483         if(this.tickable && this.editable){
12484             return;
12485         }
12486         
12487         this.collapse();
12488         // only causes errors at present
12489         //Roo.log(this.store.reader.jsonData);
12490         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12491             // fixme
12492             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12493         //}
12494         
12495         
12496     },
12497     // private
12498     onTypeAhead : function(){
12499         if(this.store.getCount() > 0){
12500             var r = this.store.getAt(0);
12501             var newValue = r.data[this.displayField];
12502             var len = newValue.length;
12503             var selStart = this.getRawValue().length;
12504             
12505             if(selStart != len){
12506                 this.setRawValue(newValue);
12507                 this.selectText(selStart, newValue.length);
12508             }
12509         }
12510     },
12511
12512     // private
12513     onSelect : function(record, index){
12514         
12515         if(this.fireEvent('beforeselect', this, record, index) !== false){
12516         
12517             this.setFromData(index > -1 ? record.data : false);
12518             
12519             this.collapse();
12520             this.fireEvent('select', this, record, index);
12521         }
12522     },
12523
12524     /**
12525      * Returns the currently selected field value or empty string if no value is set.
12526      * @return {String} value The selected value
12527      */
12528     getValue : function(){
12529         
12530         if(this.multiple){
12531             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12532         }
12533         
12534         if(this.valueField){
12535             return typeof this.value != 'undefined' ? this.value : '';
12536         }else{
12537             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12538         }
12539     },
12540
12541     /**
12542      * Clears any text/value currently set in the field
12543      */
12544     clearValue : function(){
12545         if(this.hiddenField){
12546             this.hiddenField.dom.value = '';
12547         }
12548         this.value = '';
12549         this.setRawValue('');
12550         this.lastSelectionText = '';
12551         this.lastData = false;
12552         
12553         var close = this.closeTriggerEl();
12554         
12555         if(close){
12556             close.hide();
12557         }
12558         
12559     },
12560
12561     /**
12562      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12563      * will be displayed in the field.  If the value does not match the data value of an existing item,
12564      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12565      * Otherwise the field will be blank (although the value will still be set).
12566      * @param {String} value The value to match
12567      */
12568     setValue : function(v){
12569         if(this.multiple){
12570             this.syncValue();
12571             return;
12572         }
12573         
12574         var text = v;
12575         if(this.valueField){
12576             var r = this.findRecord(this.valueField, v);
12577             if(r){
12578                 text = r.data[this.displayField];
12579             }else if(this.valueNotFoundText !== undefined){
12580                 text = this.valueNotFoundText;
12581             }
12582         }
12583         this.lastSelectionText = text;
12584         if(this.hiddenField){
12585             this.hiddenField.dom.value = v;
12586         }
12587         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12588         this.value = v;
12589         
12590         var close = this.closeTriggerEl();
12591         
12592         if(close){
12593             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12594         }
12595     },
12596     /**
12597      * @property {Object} the last set data for the element
12598      */
12599     
12600     lastData : false,
12601     /**
12602      * Sets the value of the field based on a object which is related to the record format for the store.
12603      * @param {Object} value the value to set as. or false on reset?
12604      */
12605     setFromData : function(o){
12606         
12607         if(this.multiple){
12608             this.addItem(o);
12609             return;
12610         }
12611             
12612         var dv = ''; // display value
12613         var vv = ''; // value value..
12614         this.lastData = o;
12615         if (this.displayField) {
12616             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12617         } else {
12618             // this is an error condition!!!
12619             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12620         }
12621         
12622         if(this.valueField){
12623             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12624         }
12625         
12626         var close = this.closeTriggerEl();
12627         
12628         if(close){
12629             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12630         }
12631         
12632         if(this.hiddenField){
12633             this.hiddenField.dom.value = vv;
12634             
12635             this.lastSelectionText = dv;
12636             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12637             this.value = vv;
12638             return;
12639         }
12640         // no hidden field.. - we store the value in 'value', but still display
12641         // display field!!!!
12642         this.lastSelectionText = dv;
12643         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12644         this.value = vv;
12645         
12646         
12647         
12648     },
12649     // private
12650     reset : function(){
12651         // overridden so that last data is reset..
12652         
12653         if(this.multiple){
12654             this.clearItem();
12655             return;
12656         }
12657         
12658         this.setValue(this.originalValue);
12659         this.clearInvalid();
12660         this.lastData = false;
12661         if (this.view) {
12662             this.view.clearSelections();
12663         }
12664     },
12665     // private
12666     findRecord : function(prop, value){
12667         var record;
12668         if(this.store.getCount() > 0){
12669             this.store.each(function(r){
12670                 if(r.data[prop] == value){
12671                     record = r;
12672                     return false;
12673                 }
12674                 return true;
12675             });
12676         }
12677         return record;
12678     },
12679     
12680     getName: function()
12681     {
12682         // returns hidden if it's set..
12683         if (!this.rendered) {return ''};
12684         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12685         
12686     },
12687     // private
12688     onViewMove : function(e, t){
12689         this.inKeyMode = false;
12690     },
12691
12692     // private
12693     onViewOver : function(e, t){
12694         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12695             return;
12696         }
12697         var item = this.view.findItemFromChild(t);
12698         
12699         if(item){
12700             var index = this.view.indexOf(item);
12701             this.select(index, false);
12702         }
12703     },
12704
12705     // private
12706     onViewClick : function(view, doFocus, el, e)
12707     {
12708         var index = this.view.getSelectedIndexes()[0];
12709         
12710         var r = this.store.getAt(index);
12711         
12712         if(this.tickable){
12713             
12714             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12715                 return;
12716             }
12717             
12718             var rm = false;
12719             var _this = this;
12720             
12721             Roo.each(this.tickItems, function(v,k){
12722                 
12723                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12724                     Roo.log(v);
12725                     _this.tickItems.splice(k, 1);
12726                     
12727                     if(typeof(e) == 'undefined' && view == false){
12728                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12729                     }
12730                     
12731                     rm = true;
12732                     return;
12733                 }
12734             });
12735             
12736             if(rm){
12737                 return;
12738             }
12739             
12740             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12741                 this.tickItems.push(r.data);
12742             }
12743             
12744             if(typeof(e) == 'undefined' && view == false){
12745                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12746             }
12747                     
12748             return;
12749         }
12750         
12751         if(r){
12752             this.onSelect(r, index);
12753         }
12754         if(doFocus !== false && !this.blockFocus){
12755             this.inputEl().focus();
12756         }
12757     },
12758
12759     // private
12760     restrictHeight : function(){
12761         //this.innerList.dom.style.height = '';
12762         //var inner = this.innerList.dom;
12763         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12764         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12765         //this.list.beginUpdate();
12766         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12767         this.list.alignTo(this.inputEl(), this.listAlign);
12768         this.list.alignTo(this.inputEl(), this.listAlign);
12769         //this.list.endUpdate();
12770     },
12771
12772     // private
12773     onEmptyResults : function(){
12774         
12775         if(this.tickable && this.editable){
12776             this.restrictHeight();
12777             return;
12778         }
12779         
12780         this.collapse();
12781     },
12782
12783     /**
12784      * Returns true if the dropdown list is expanded, else false.
12785      */
12786     isExpanded : function(){
12787         return this.list.isVisible();
12788     },
12789
12790     /**
12791      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12792      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12793      * @param {String} value The data value of the item to select
12794      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12795      * selected item if it is not currently in view (defaults to true)
12796      * @return {Boolean} True if the value matched an item in the list, else false
12797      */
12798     selectByValue : function(v, scrollIntoView){
12799         if(v !== undefined && v !== null){
12800             var r = this.findRecord(this.valueField || this.displayField, v);
12801             if(r){
12802                 this.select(this.store.indexOf(r), scrollIntoView);
12803                 return true;
12804             }
12805         }
12806         return false;
12807     },
12808
12809     /**
12810      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12811      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12812      * @param {Number} index The zero-based index of the list item to select
12813      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12814      * selected item if it is not currently in view (defaults to true)
12815      */
12816     select : function(index, scrollIntoView){
12817         this.selectedIndex = index;
12818         this.view.select(index);
12819         if(scrollIntoView !== false){
12820             var el = this.view.getNode(index);
12821             /*
12822              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12823              */
12824             if(el){
12825                 this.list.scrollChildIntoView(el, false);
12826             }
12827         }
12828     },
12829
12830     // private
12831     selectNext : function(){
12832         var ct = this.store.getCount();
12833         if(ct > 0){
12834             if(this.selectedIndex == -1){
12835                 this.select(0);
12836             }else if(this.selectedIndex < ct-1){
12837                 this.select(this.selectedIndex+1);
12838             }
12839         }
12840     },
12841
12842     // private
12843     selectPrev : function(){
12844         var ct = this.store.getCount();
12845         if(ct > 0){
12846             if(this.selectedIndex == -1){
12847                 this.select(0);
12848             }else if(this.selectedIndex != 0){
12849                 this.select(this.selectedIndex-1);
12850             }
12851         }
12852     },
12853
12854     // private
12855     onKeyUp : function(e){
12856         if(this.editable !== false && !e.isSpecialKey()){
12857             this.lastKey = e.getKey();
12858             this.dqTask.delay(this.queryDelay);
12859         }
12860     },
12861
12862     // private
12863     validateBlur : function(){
12864         return !this.list || !this.list.isVisible();   
12865     },
12866
12867     // private
12868     initQuery : function(){
12869         
12870         var v = this.getRawValue();
12871         
12872         if(this.tickable && this.editable){
12873             v = this.tickableInputEl().getValue();
12874         }
12875         
12876         this.doQuery(v);
12877     },
12878
12879     // private
12880     doForce : function(){
12881         if(this.inputEl().dom.value.length > 0){
12882             this.inputEl().dom.value =
12883                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12884              
12885         }
12886     },
12887
12888     /**
12889      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12890      * query allowing the query action to be canceled if needed.
12891      * @param {String} query The SQL query to execute
12892      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12893      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12894      * saved in the current store (defaults to false)
12895      */
12896     doQuery : function(q, forceAll){
12897         
12898         if(q === undefined || q === null){
12899             q = '';
12900         }
12901         var qe = {
12902             query: q,
12903             forceAll: forceAll,
12904             combo: this,
12905             cancel:false
12906         };
12907         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12908             return false;
12909         }
12910         q = qe.query;
12911         
12912         forceAll = qe.forceAll;
12913         if(forceAll === true || (q.length >= this.minChars)){
12914             
12915             this.hasQuery = true;
12916             
12917             if(this.lastQuery != q || this.alwaysQuery){
12918                 this.lastQuery = q;
12919                 if(this.mode == 'local'){
12920                     this.selectedIndex = -1;
12921                     if(forceAll){
12922                         this.store.clearFilter();
12923                     }else{
12924                         
12925                         if(this.specialFilter){
12926                             this.fireEvent('specialfilter', this);
12927                             this.onLoad();
12928                             return;
12929                         }
12930                         
12931                         this.store.filter(this.displayField, q);
12932                     }
12933                     
12934                     this.store.fireEvent("datachanged", this.store);
12935                     
12936                     this.onLoad();
12937                     
12938                     
12939                 }else{
12940                     
12941                     this.store.baseParams[this.queryParam] = q;
12942                     
12943                     var options = {params : this.getParams(q)};
12944                     
12945                     if(this.loadNext){
12946                         options.add = true;
12947                         options.params.start = this.page * this.pageSize;
12948                     }
12949                     
12950                     this.store.load(options);
12951                     
12952                     /*
12953                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12954                      *  we should expand the list on onLoad
12955                      *  so command out it
12956                      */
12957 //                    this.expand();
12958                 }
12959             }else{
12960                 this.selectedIndex = -1;
12961                 this.onLoad();   
12962             }
12963         }
12964         
12965         this.loadNext = false;
12966     },
12967     
12968     // private
12969     getParams : function(q){
12970         var p = {};
12971         //p[this.queryParam] = q;
12972         
12973         if(this.pageSize){
12974             p.start = 0;
12975             p.limit = this.pageSize;
12976         }
12977         return p;
12978     },
12979
12980     /**
12981      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12982      */
12983     collapse : function(){
12984         if(!this.isExpanded()){
12985             return;
12986         }
12987         
12988         this.list.hide();
12989         
12990         if(this.tickable){
12991             this.hasFocus = false;
12992             this.okBtn.hide();
12993             this.cancelBtn.hide();
12994             this.trigger.show();
12995             
12996             if(this.editable){
12997                 this.tickableInputEl().dom.value = '';
12998                 this.tickableInputEl().blur();
12999             }
13000             
13001         }
13002         
13003         Roo.get(document).un('mousedown', this.collapseIf, this);
13004         Roo.get(document).un('mousewheel', this.collapseIf, this);
13005         if (!this.editable) {
13006             Roo.get(document).un('keydown', this.listKeyPress, this);
13007         }
13008         this.fireEvent('collapse', this);
13009     },
13010
13011     // private
13012     collapseIf : function(e){
13013         var in_combo  = e.within(this.el);
13014         var in_list =  e.within(this.list);
13015         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13016         
13017         if (in_combo || in_list || is_list) {
13018             //e.stopPropagation();
13019             return;
13020         }
13021         
13022         if(this.tickable){
13023             this.onTickableFooterButtonClick(e, false, false);
13024         }
13025
13026         this.collapse();
13027         
13028     },
13029
13030     /**
13031      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13032      */
13033     expand : function(){
13034        
13035         if(this.isExpanded() || !this.hasFocus){
13036             return;
13037         }
13038         
13039         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13040         this.list.setWidth(lw);
13041         
13042         
13043          Roo.log('expand');
13044         
13045         this.list.show();
13046         
13047         this.restrictHeight();
13048         
13049         if(this.tickable){
13050             
13051             this.tickItems = Roo.apply([], this.item);
13052             
13053             this.okBtn.show();
13054             this.cancelBtn.show();
13055             this.trigger.hide();
13056             
13057             if(this.editable){
13058                 this.tickableInputEl().focus();
13059             }
13060             
13061         }
13062         
13063         Roo.get(document).on('mousedown', this.collapseIf, this);
13064         Roo.get(document).on('mousewheel', this.collapseIf, this);
13065         if (!this.editable) {
13066             Roo.get(document).on('keydown', this.listKeyPress, this);
13067         }
13068         
13069         this.fireEvent('expand', this);
13070     },
13071
13072     // private
13073     // Implements the default empty TriggerField.onTriggerClick function
13074     onTriggerClick : function(e)
13075     {
13076         Roo.log('trigger click');
13077         
13078         if(this.disabled || !this.triggerList){
13079             return;
13080         }
13081         
13082         this.page = 0;
13083         this.loadNext = false;
13084         
13085         if(this.isExpanded()){
13086             this.collapse();
13087             if (!this.blockFocus) {
13088                 this.inputEl().focus();
13089             }
13090             
13091         }else {
13092             this.hasFocus = true;
13093             if(this.triggerAction == 'all') {
13094                 this.doQuery(this.allQuery, true);
13095             } else {
13096                 this.doQuery(this.getRawValue());
13097             }
13098             if (!this.blockFocus) {
13099                 this.inputEl().focus();
13100             }
13101         }
13102     },
13103     
13104     onTickableTriggerClick : function(e)
13105     {
13106         if(this.disabled){
13107             return;
13108         }
13109         
13110         this.page = 0;
13111         this.loadNext = false;
13112         this.hasFocus = true;
13113         
13114         if(this.triggerAction == 'all') {
13115             this.doQuery(this.allQuery, true);
13116         } else {
13117             this.doQuery(this.getRawValue());
13118         }
13119     },
13120     
13121     onSearchFieldClick : function(e)
13122     {
13123         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13124             this.onTickableFooterButtonClick(e, false, false);
13125             return;
13126         }
13127         
13128         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13129             return;
13130         }
13131         
13132         this.page = 0;
13133         this.loadNext = false;
13134         this.hasFocus = true;
13135         
13136         if(this.triggerAction == 'all') {
13137             this.doQuery(this.allQuery, true);
13138         } else {
13139             this.doQuery(this.getRawValue());
13140         }
13141     },
13142     
13143     listKeyPress : function(e)
13144     {
13145         //Roo.log('listkeypress');
13146         // scroll to first matching element based on key pres..
13147         if (e.isSpecialKey()) {
13148             return false;
13149         }
13150         var k = String.fromCharCode(e.getKey()).toUpperCase();
13151         //Roo.log(k);
13152         var match  = false;
13153         var csel = this.view.getSelectedNodes();
13154         var cselitem = false;
13155         if (csel.length) {
13156             var ix = this.view.indexOf(csel[0]);
13157             cselitem  = this.store.getAt(ix);
13158             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13159                 cselitem = false;
13160             }
13161             
13162         }
13163         
13164         this.store.each(function(v) { 
13165             if (cselitem) {
13166                 // start at existing selection.
13167                 if (cselitem.id == v.id) {
13168                     cselitem = false;
13169                 }
13170                 return true;
13171             }
13172                 
13173             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13174                 match = this.store.indexOf(v);
13175                 return false;
13176             }
13177             return true;
13178         }, this);
13179         
13180         if (match === false) {
13181             return true; // no more action?
13182         }
13183         // scroll to?
13184         this.view.select(match);
13185         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13186         sn.scrollIntoView(sn.dom.parentNode, false);
13187     },
13188     
13189     onViewScroll : function(e, t){
13190         
13191         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){
13192             return;
13193         }
13194         
13195         this.hasQuery = true;
13196         
13197         this.loading = this.list.select('.loading', true).first();
13198         
13199         if(this.loading === null){
13200             this.list.createChild({
13201                 tag: 'div',
13202                 cls: 'loading select2-more-results select2-active',
13203                 html: 'Loading more results...'
13204             });
13205             
13206             this.loading = this.list.select('.loading', true).first();
13207             
13208             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13209             
13210             this.loading.hide();
13211         }
13212         
13213         this.loading.show();
13214         
13215         var _combo = this;
13216         
13217         this.page++;
13218         this.loadNext = true;
13219         
13220         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13221         
13222         return;
13223     },
13224     
13225     addItem : function(o)
13226     {   
13227         var dv = ''; // display value
13228         
13229         if (this.displayField) {
13230             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13231         } else {
13232             // this is an error condition!!!
13233             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13234         }
13235         
13236         if(!dv.length){
13237             return;
13238         }
13239         
13240         var choice = this.choices.createChild({
13241             tag: 'li',
13242             cls: 'select2-search-choice',
13243             cn: [
13244                 {
13245                     tag: 'div',
13246                     html: dv
13247                 },
13248                 {
13249                     tag: 'a',
13250                     href: '#',
13251                     cls: 'select2-search-choice-close',
13252                     tabindex: '-1'
13253                 }
13254             ]
13255             
13256         }, this.searchField);
13257         
13258         var close = choice.select('a.select2-search-choice-close', true).first();
13259         
13260         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13261         
13262         this.item.push(o);
13263         
13264         this.lastData = o;
13265         
13266         this.syncValue();
13267         
13268         this.inputEl().dom.value = '';
13269         
13270         this.validate();
13271     },
13272     
13273     onRemoveItem : function(e, _self, o)
13274     {
13275         e.preventDefault();
13276         
13277         this.lastItem = Roo.apply([], this.item);
13278         
13279         var index = this.item.indexOf(o.data) * 1;
13280         
13281         if( index < 0){
13282             Roo.log('not this item?!');
13283             return;
13284         }
13285         
13286         this.item.splice(index, 1);
13287         o.item.remove();
13288         
13289         this.syncValue();
13290         
13291         this.fireEvent('remove', this, e);
13292         
13293         this.validate();
13294         
13295     },
13296     
13297     syncValue : function()
13298     {
13299         if(!this.item.length){
13300             this.clearValue();
13301             return;
13302         }
13303             
13304         var value = [];
13305         var _this = this;
13306         Roo.each(this.item, function(i){
13307             if(_this.valueField){
13308                 value.push(i[_this.valueField]);
13309                 return;
13310             }
13311
13312             value.push(i);
13313         });
13314
13315         this.value = value.join(',');
13316
13317         if(this.hiddenField){
13318             this.hiddenField.dom.value = this.value;
13319         }
13320         
13321         this.store.fireEvent("datachanged", this.store);
13322     },
13323     
13324     clearItem : function()
13325     {
13326         if(!this.multiple){
13327             return;
13328         }
13329         
13330         this.item = [];
13331         
13332         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13333            c.remove();
13334         });
13335         
13336         this.syncValue();
13337         
13338         this.validate();
13339         
13340         if(this.tickable && !Roo.isTouch){
13341             this.view.refresh();
13342         }
13343     },
13344     
13345     inputEl: function ()
13346     {
13347         if(Roo.isTouch && this.mobileTouchView){
13348             return this.el.select('input.form-control',true).first();
13349         }
13350         
13351         if(this.tickable){
13352             return this.searchField;
13353         }
13354         
13355         return this.el.select('input.form-control',true).first();
13356     },
13357     
13358     
13359     onTickableFooterButtonClick : function(e, btn, el)
13360     {
13361         e.preventDefault();
13362         
13363         this.lastItem = Roo.apply([], this.item);
13364         
13365         if(btn && btn.name == 'cancel'){
13366             this.tickItems = Roo.apply([], this.item);
13367             this.collapse();
13368             return;
13369         }
13370         
13371         this.clearItem();
13372         
13373         var _this = this;
13374         
13375         Roo.each(this.tickItems, function(o){
13376             _this.addItem(o);
13377         });
13378         
13379         this.collapse();
13380         
13381     },
13382     
13383     validate : function()
13384     {
13385         var v = this.getRawValue();
13386         
13387         if(this.multiple){
13388             v = this.getValue();
13389         }
13390         
13391         if(this.disabled || this.allowBlank || v.length){
13392             this.markValid();
13393             return true;
13394         }
13395         
13396         this.markInvalid();
13397         return false;
13398     },
13399     
13400     tickableInputEl : function()
13401     {
13402         if(!this.tickable || !this.editable){
13403             return this.inputEl();
13404         }
13405         
13406         return this.inputEl().select('.select2-search-field-input', true).first();
13407     },
13408     
13409     
13410     getAutoCreateTouchView : function()
13411     {
13412         var id = Roo.id();
13413         
13414         var cfg = {
13415             cls: 'form-group' //input-group
13416         };
13417         
13418         var input =  {
13419             tag: 'input',
13420             id : id,
13421             type : this.inputType,
13422             cls : 'form-control x-combo-noedit',
13423             autocomplete: 'new-password',
13424             placeholder : this.placeholder || '',
13425             readonly : true
13426         };
13427         
13428         if (this.name) {
13429             input.name = this.name;
13430         }
13431         
13432         if (this.size) {
13433             input.cls += ' input-' + this.size;
13434         }
13435         
13436         if (this.disabled) {
13437             input.disabled = true;
13438         }
13439         
13440         var inputblock = {
13441             cls : '',
13442             cn : [
13443                 input
13444             ]
13445         };
13446         
13447         if(this.before){
13448             inputblock.cls += ' input-group';
13449             
13450             inputblock.cn.unshift({
13451                 tag :'span',
13452                 cls : 'input-group-addon',
13453                 html : this.before
13454             });
13455         }
13456         
13457         if(this.removable && !this.multiple){
13458             inputblock.cls += ' roo-removable';
13459             
13460             inputblock.cn.push({
13461                 tag: 'button',
13462                 html : 'x',
13463                 cls : 'roo-combo-removable-btn close'
13464             });
13465         }
13466
13467         if(this.hasFeedback && !this.allowBlank){
13468             
13469             inputblock.cls += ' has-feedback';
13470             
13471             inputblock.cn.push({
13472                 tag: 'span',
13473                 cls: 'glyphicon form-control-feedback'
13474             });
13475             
13476         }
13477         
13478         if (this.after) {
13479             
13480             inputblock.cls += (this.before) ? '' : ' input-group';
13481             
13482             inputblock.cn.push({
13483                 tag :'span',
13484                 cls : 'input-group-addon',
13485                 html : this.after
13486             });
13487         }
13488
13489         var box = {
13490             tag: 'div',
13491             cn: [
13492                 {
13493                     tag: 'input',
13494                     type : 'hidden',
13495                     cls: 'form-hidden-field'
13496                 },
13497                 inputblock
13498             ]
13499             
13500         };
13501         
13502         if(this.multiple){
13503             box = {
13504                 tag: 'div',
13505                 cn: [
13506                     {
13507                         tag: 'input',
13508                         type : 'hidden',
13509                         cls: 'form-hidden-field'
13510                     },
13511                     {
13512                         tag: 'ul',
13513                         cls: 'select2-choices',
13514                         cn:[
13515                             {
13516                                 tag: 'li',
13517                                 cls: 'select2-search-field',
13518                                 cn: [
13519
13520                                     inputblock
13521                                 ]
13522                             }
13523                         ]
13524                     }
13525                 ]
13526             }
13527         };
13528         
13529         var combobox = {
13530             cls: 'select2-container input-group',
13531             cn: [
13532                 box
13533             ]
13534         };
13535         
13536         if(this.multiple){
13537             combobox.cls += ' select2-container-multi';
13538         }
13539         
13540         var align = this.labelAlign || this.parentLabelAlign();
13541         
13542         cfg.cn = combobox;
13543         
13544         if(this.fieldLabel.length){
13545             
13546             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13547             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13548             
13549             cfg.cn = [
13550                 {
13551                     tag: 'label',
13552                     cls : 'control-label ' + lw,
13553                     html : this.fieldLabel
13554
13555                 },
13556                 {
13557                     cls : cw, 
13558                     cn: [
13559                         combobox
13560                     ]
13561                 }
13562             ];
13563         }
13564         
13565         var settings = this;
13566         
13567         ['xs','sm','md','lg'].map(function(size){
13568             if (settings[size]) {
13569                 cfg.cls += ' col-' + size + '-' + settings[size];
13570             }
13571         });
13572         
13573         return cfg;
13574     },
13575     
13576     initTouchView : function()
13577     {
13578         this.renderTouchView();
13579         
13580         this.touchViewEl.on('scroll', function(){
13581             this.el.dom.scrollTop = 0;
13582         }, this);
13583         
13584         this.inputEl().on("click", this.showTouchView, this);
13585         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13586         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13587         
13588         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13589         
13590         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13591         this.store.on('load', this.onTouchViewLoad, this);
13592         this.store.on('loadexception', this.onTouchViewLoadException, this);
13593         
13594         if(this.hiddenName){
13595             
13596             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13597             
13598             this.hiddenField.dom.value =
13599                 this.hiddenValue !== undefined ? this.hiddenValue :
13600                 this.value !== undefined ? this.value : '';
13601         
13602             this.el.dom.removeAttribute('name');
13603             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13604         }
13605         
13606         if(this.multiple){
13607             this.choices = this.el.select('ul.select2-choices', true).first();
13608             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13609         }
13610         
13611         if(this.removable && !this.multiple){
13612             var close = this.closeTriggerEl();
13613             if(close){
13614                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13615                 close.on('click', this.removeBtnClick, this, close);
13616             }
13617         }
13618         
13619         return;
13620         
13621         
13622     },
13623     
13624     renderTouchView : function()
13625     {
13626         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13627         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13628         
13629         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13630         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13631         
13632         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13633         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13634         this.touchViewBodyEl.setStyle('overflow', 'auto');
13635         
13636         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13637         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13638         
13639         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13640         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13641         
13642     },
13643     
13644     showTouchView : function()
13645     {
13646         this.touchViewHeaderEl.hide();
13647
13648         if(this.fieldLabel.length){
13649             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13650             this.touchViewHeaderEl.show();
13651         }
13652
13653         this.touchViewEl.show();
13654
13655         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13656         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13657
13658         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13659
13660         if(this.fieldLabel.length){
13661             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13662         }
13663         
13664         this.touchViewBodyEl.setHeight(bodyHeight);
13665
13666         if(this.animate){
13667             var _this = this;
13668             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13669         }else{
13670             this.touchViewEl.addClass('in');
13671         }
13672
13673         this.doTouchViewQuery();
13674         
13675     },
13676     
13677     hideTouchView : function()
13678     {
13679         this.touchViewEl.removeClass('in');
13680
13681         if(this.animate){
13682             var _this = this;
13683             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13684         }else{
13685             this.touchViewEl.setStyle('display', 'none');
13686         }
13687         
13688     },
13689     
13690     setTouchViewValue : function()
13691     {
13692         if(this.multiple){
13693             this.clearItem();
13694         
13695             var _this = this;
13696
13697             Roo.each(this.tickItems, function(o){
13698                 this.addItem(o);
13699             }, this);
13700         }
13701         
13702         this.hideTouchView();
13703     },
13704     
13705     doTouchViewQuery : function()
13706     {
13707         var qe = {
13708             query: '',
13709             forceAll: true,
13710             combo: this,
13711             cancel:false
13712         };
13713         
13714         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13715             return false;
13716         }
13717         
13718         if(!this.alwaysQuery || this.mode == 'local'){
13719             this.onTouchViewLoad();
13720             return;
13721         }
13722         
13723         this.store.load();
13724     },
13725     
13726     onTouchViewBeforeLoad : function(combo,opts)
13727     {
13728         return;
13729     },
13730
13731     // private
13732     onTouchViewLoad : function()
13733     {
13734         if(this.store.getCount() < 1){
13735             this.onTouchViewEmptyResults();
13736             return;
13737         }
13738         
13739         this.clearTouchView();
13740         
13741         var rawValue = this.getRawValue();
13742         
13743         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13744         
13745         this.tickItems = [];
13746         
13747         this.store.data.each(function(d, rowIndex){
13748             var row = this.touchViewListGroup.createChild(template);
13749             
13750             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13751                 var cfg = {
13752                     data : d.data,
13753                     html : d.data[this.displayField]
13754                 };
13755                 
13756                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13757                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13758                 }
13759             }
13760             
13761             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13762                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13763             }
13764             
13765             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13766                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13767                 this.tickItems.push(d.data);
13768             }
13769             
13770             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13771             
13772         }, this);
13773         
13774         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13775         
13776         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13777
13778         if(this.fieldLabel.length){
13779             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13780         }
13781
13782         var listHeight = this.touchViewListGroup.getHeight();
13783         
13784         var _this = this;
13785         
13786         if(firstChecked && listHeight > bodyHeight){
13787             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13788         }
13789         
13790     },
13791     
13792     onTouchViewLoadException : function()
13793     {
13794         this.hideTouchView();
13795     },
13796     
13797     onTouchViewEmptyResults : function()
13798     {
13799         this.clearTouchView();
13800         
13801         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13802         
13803         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13804         
13805     },
13806     
13807     clearTouchView : function()
13808     {
13809         this.touchViewListGroup.dom.innerHTML = '';
13810     },
13811     
13812     onTouchViewClick : function(e, el, o)
13813     {
13814         e.preventDefault();
13815         
13816         var row = o.row;
13817         var rowIndex = o.rowIndex;
13818         
13819         var r = this.store.getAt(rowIndex);
13820         
13821         if(!this.multiple){
13822             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13823                 c.dom.removeAttribute('checked');
13824             }, this);
13825             
13826             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13827         
13828             this.setFromData(r.data);
13829             
13830             var close = this.closeTriggerEl();
13831         
13832             if(close){
13833                 close.show();
13834             }
13835
13836             this.hideTouchView();
13837             
13838             this.fireEvent('select', this, r, rowIndex);
13839             
13840             return;
13841         }
13842         
13843         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13844             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13845             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13846             return;
13847         }
13848         
13849         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13850         this.addItem(r.data);
13851         this.tickItems.push(r.data);
13852         
13853     }
13854     
13855
13856     /** 
13857     * @cfg {Boolean} grow 
13858     * @hide 
13859     */
13860     /** 
13861     * @cfg {Number} growMin 
13862     * @hide 
13863     */
13864     /** 
13865     * @cfg {Number} growMax 
13866     * @hide 
13867     */
13868     /**
13869      * @hide
13870      * @method autoSize
13871      */
13872 });
13873
13874 Roo.apply(Roo.bootstrap.ComboBox,  {
13875     
13876     header : {
13877         tag: 'div',
13878         cls: 'modal-header',
13879         cn: [
13880             {
13881                 tag: 'h4',
13882                 cls: 'modal-title'
13883             }
13884         ]
13885     },
13886     
13887     body : {
13888         tag: 'div',
13889         cls: 'modal-body',
13890         cn: [
13891             {
13892                 tag: 'ul',
13893                 cls: 'list-group'
13894             }
13895         ]
13896     },
13897     
13898     listItemRadio : {
13899         tag: 'li',
13900         cls: 'list-group-item',
13901         cn: [
13902             {
13903                 tag: 'span',
13904                 cls: 'roo-combobox-list-group-item-value'
13905             },
13906             {
13907                 tag: 'div',
13908                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13909                 cn: [
13910                     {
13911                         tag: 'input',
13912                         type: 'radio'
13913                     },
13914                     {
13915                         tag: 'label'
13916                     }
13917                 ]
13918             }
13919         ]
13920     },
13921     
13922     listItemCheckbox : {
13923         tag: 'li',
13924         cls: 'list-group-item',
13925         cn: [
13926             {
13927                 tag: 'span',
13928                 cls: 'roo-combobox-list-group-item-value'
13929             },
13930             {
13931                 tag: 'div',
13932                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13933                 cn: [
13934                     {
13935                         tag: 'input',
13936                         type: 'checkbox'
13937                     },
13938                     {
13939                         tag: 'label'
13940                     }
13941                 ]
13942             }
13943         ]
13944     },
13945     
13946     emptyResult : {
13947         tag: 'div',
13948         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13949     },
13950     
13951     footer : {
13952         tag: 'div',
13953         cls: 'modal-footer',
13954         cn: [
13955             {
13956                 tag: 'div',
13957                 cls: 'row',
13958                 cn: [
13959                     {
13960                         tag: 'div',
13961                         cls: 'col-xs-6 text-left',
13962                         cn: {
13963                             tag: 'button',
13964                             cls: 'btn btn-danger roo-touch-view-cancel',
13965                             html: 'Cancel'
13966                         }
13967                     },
13968                     {
13969                         tag: 'div',
13970                         cls: 'col-xs-6 text-right',
13971                         cn: {
13972                             tag: 'button',
13973                             cls: 'btn btn-success roo-touch-view-ok',
13974                             html: 'OK'
13975                         }
13976                     }
13977                 ]
13978             }
13979         ]
13980         
13981     }
13982 });
13983
13984 Roo.apply(Roo.bootstrap.ComboBox,  {
13985     
13986     touchViewTemplate : {
13987         tag: 'div',
13988         cls: 'modal fade roo-combobox-touch-view',
13989         cn: [
13990             {
13991                 tag: 'div',
13992                 cls: 'modal-dialog',
13993                 style : 'position:fixed', // we have to fix position....
13994                 cn: [
13995                     {
13996                         tag: 'div',
13997                         cls: 'modal-content',
13998                         cn: [
13999                             Roo.bootstrap.ComboBox.header,
14000                             Roo.bootstrap.ComboBox.body,
14001                             Roo.bootstrap.ComboBox.footer
14002                         ]
14003                     }
14004                 ]
14005             }
14006         ]
14007     }
14008 });/*
14009  * Based on:
14010  * Ext JS Library 1.1.1
14011  * Copyright(c) 2006-2007, Ext JS, LLC.
14012  *
14013  * Originally Released Under LGPL - original licence link has changed is not relivant.
14014  *
14015  * Fork - LGPL
14016  * <script type="text/javascript">
14017  */
14018
14019 /**
14020  * @class Roo.View
14021  * @extends Roo.util.Observable
14022  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14023  * This class also supports single and multi selection modes. <br>
14024  * Create a data model bound view:
14025  <pre><code>
14026  var store = new Roo.data.Store(...);
14027
14028  var view = new Roo.View({
14029     el : "my-element",
14030     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14031  
14032     singleSelect: true,
14033     selectedClass: "ydataview-selected",
14034     store: store
14035  });
14036
14037  // listen for node click?
14038  view.on("click", function(vw, index, node, e){
14039  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14040  });
14041
14042  // load XML data
14043  dataModel.load("foobar.xml");
14044  </code></pre>
14045  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14046  * <br><br>
14047  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14048  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14049  * 
14050  * Note: old style constructor is still suported (container, template, config)
14051  * 
14052  * @constructor
14053  * Create a new View
14054  * @param {Object} config The config object
14055  * 
14056  */
14057 Roo.View = function(config, depreciated_tpl, depreciated_config){
14058     
14059     this.parent = false;
14060     
14061     if (typeof(depreciated_tpl) == 'undefined') {
14062         // new way.. - universal constructor.
14063         Roo.apply(this, config);
14064         this.el  = Roo.get(this.el);
14065     } else {
14066         // old format..
14067         this.el  = Roo.get(config);
14068         this.tpl = depreciated_tpl;
14069         Roo.apply(this, depreciated_config);
14070     }
14071     this.wrapEl  = this.el.wrap().wrap();
14072     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14073     
14074     
14075     if(typeof(this.tpl) == "string"){
14076         this.tpl = new Roo.Template(this.tpl);
14077     } else {
14078         // support xtype ctors..
14079         this.tpl = new Roo.factory(this.tpl, Roo);
14080     }
14081     
14082     
14083     this.tpl.compile();
14084     
14085     /** @private */
14086     this.addEvents({
14087         /**
14088          * @event beforeclick
14089          * Fires before a click is processed. Returns false to cancel the default action.
14090          * @param {Roo.View} this
14091          * @param {Number} index The index of the target node
14092          * @param {HTMLElement} node The target node
14093          * @param {Roo.EventObject} e The raw event object
14094          */
14095             "beforeclick" : true,
14096         /**
14097          * @event click
14098          * Fires when a template node is clicked.
14099          * @param {Roo.View} this
14100          * @param {Number} index The index of the target node
14101          * @param {HTMLElement} node The target node
14102          * @param {Roo.EventObject} e The raw event object
14103          */
14104             "click" : true,
14105         /**
14106          * @event dblclick
14107          * Fires when a template node is double clicked.
14108          * @param {Roo.View} this
14109          * @param {Number} index The index of the target node
14110          * @param {HTMLElement} node The target node
14111          * @param {Roo.EventObject} e The raw event object
14112          */
14113             "dblclick" : true,
14114         /**
14115          * @event contextmenu
14116          * Fires when a template node is right clicked.
14117          * @param {Roo.View} this
14118          * @param {Number} index The index of the target node
14119          * @param {HTMLElement} node The target node
14120          * @param {Roo.EventObject} e The raw event object
14121          */
14122             "contextmenu" : true,
14123         /**
14124          * @event selectionchange
14125          * Fires when the selected nodes change.
14126          * @param {Roo.View} this
14127          * @param {Array} selections Array of the selected nodes
14128          */
14129             "selectionchange" : true,
14130     
14131         /**
14132          * @event beforeselect
14133          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14134          * @param {Roo.View} this
14135          * @param {HTMLElement} node The node to be selected
14136          * @param {Array} selections Array of currently selected nodes
14137          */
14138             "beforeselect" : true,
14139         /**
14140          * @event preparedata
14141          * Fires on every row to render, to allow you to change the data.
14142          * @param {Roo.View} this
14143          * @param {Object} data to be rendered (change this)
14144          */
14145           "preparedata" : true
14146           
14147           
14148         });
14149
14150
14151
14152     this.el.on({
14153         "click": this.onClick,
14154         "dblclick": this.onDblClick,
14155         "contextmenu": this.onContextMenu,
14156         scope:this
14157     });
14158
14159     this.selections = [];
14160     this.nodes = [];
14161     this.cmp = new Roo.CompositeElementLite([]);
14162     if(this.store){
14163         this.store = Roo.factory(this.store, Roo.data);
14164         this.setStore(this.store, true);
14165     }
14166     
14167     if ( this.footer && this.footer.xtype) {
14168            
14169          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14170         
14171         this.footer.dataSource = this.store;
14172         this.footer.container = fctr;
14173         this.footer = Roo.factory(this.footer, Roo);
14174         fctr.insertFirst(this.el);
14175         
14176         // this is a bit insane - as the paging toolbar seems to detach the el..
14177 //        dom.parentNode.parentNode.parentNode
14178          // they get detached?
14179     }
14180     
14181     
14182     Roo.View.superclass.constructor.call(this);
14183     
14184     
14185 };
14186
14187 Roo.extend(Roo.View, Roo.util.Observable, {
14188     
14189      /**
14190      * @cfg {Roo.data.Store} store Data store to load data from.
14191      */
14192     store : false,
14193     
14194     /**
14195      * @cfg {String|Roo.Element} el The container element.
14196      */
14197     el : '',
14198     
14199     /**
14200      * @cfg {String|Roo.Template} tpl The template used by this View 
14201      */
14202     tpl : false,
14203     /**
14204      * @cfg {String} dataName the named area of the template to use as the data area
14205      *                          Works with domtemplates roo-name="name"
14206      */
14207     dataName: false,
14208     /**
14209      * @cfg {String} selectedClass The css class to add to selected nodes
14210      */
14211     selectedClass : "x-view-selected",
14212      /**
14213      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14214      */
14215     emptyText : "",
14216     
14217     /**
14218      * @cfg {String} text to display on mask (default Loading)
14219      */
14220     mask : false,
14221     /**
14222      * @cfg {Boolean} multiSelect Allow multiple selection
14223      */
14224     multiSelect : false,
14225     /**
14226      * @cfg {Boolean} singleSelect Allow single selection
14227      */
14228     singleSelect:  false,
14229     
14230     /**
14231      * @cfg {Boolean} toggleSelect - selecting 
14232      */
14233     toggleSelect : false,
14234     
14235     /**
14236      * @cfg {Boolean} tickable - selecting 
14237      */
14238     tickable : false,
14239     
14240     /**
14241      * Returns the element this view is bound to.
14242      * @return {Roo.Element}
14243      */
14244     getEl : function(){
14245         return this.wrapEl;
14246     },
14247     
14248     
14249
14250     /**
14251      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14252      */
14253     refresh : function(){
14254         //Roo.log('refresh');
14255         var t = this.tpl;
14256         
14257         // if we are using something like 'domtemplate', then
14258         // the what gets used is:
14259         // t.applySubtemplate(NAME, data, wrapping data..)
14260         // the outer template then get' applied with
14261         //     the store 'extra data'
14262         // and the body get's added to the
14263         //      roo-name="data" node?
14264         //      <span class='roo-tpl-{name}'></span> ?????
14265         
14266         
14267         
14268         this.clearSelections();
14269         this.el.update("");
14270         var html = [];
14271         var records = this.store.getRange();
14272         if(records.length < 1) {
14273             
14274             // is this valid??  = should it render a template??
14275             
14276             this.el.update(this.emptyText);
14277             return;
14278         }
14279         var el = this.el;
14280         if (this.dataName) {
14281             this.el.update(t.apply(this.store.meta)); //????
14282             el = this.el.child('.roo-tpl-' + this.dataName);
14283         }
14284         
14285         for(var i = 0, len = records.length; i < len; i++){
14286             var data = this.prepareData(records[i].data, i, records[i]);
14287             this.fireEvent("preparedata", this, data, i, records[i]);
14288             
14289             var d = Roo.apply({}, data);
14290             
14291             if(this.tickable){
14292                 Roo.apply(d, {'roo-id' : Roo.id()});
14293                 
14294                 var _this = this;
14295             
14296                 Roo.each(this.parent.item, function(item){
14297                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14298                         return;
14299                     }
14300                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14301                 });
14302             }
14303             
14304             html[html.length] = Roo.util.Format.trim(
14305                 this.dataName ?
14306                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14307                     t.apply(d)
14308             );
14309         }
14310         
14311         
14312         
14313         el.update(html.join(""));
14314         this.nodes = el.dom.childNodes;
14315         this.updateIndexes(0);
14316     },
14317     
14318
14319     /**
14320      * Function to override to reformat the data that is sent to
14321      * the template for each node.
14322      * DEPRICATED - use the preparedata event handler.
14323      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14324      * a JSON object for an UpdateManager bound view).
14325      */
14326     prepareData : function(data, index, record)
14327     {
14328         this.fireEvent("preparedata", this, data, index, record);
14329         return data;
14330     },
14331
14332     onUpdate : function(ds, record){
14333         // Roo.log('on update');   
14334         this.clearSelections();
14335         var index = this.store.indexOf(record);
14336         var n = this.nodes[index];
14337         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14338         n.parentNode.removeChild(n);
14339         this.updateIndexes(index, index);
14340     },
14341
14342     
14343     
14344 // --------- FIXME     
14345     onAdd : function(ds, records, index)
14346     {
14347         //Roo.log(['on Add', ds, records, index] );        
14348         this.clearSelections();
14349         if(this.nodes.length == 0){
14350             this.refresh();
14351             return;
14352         }
14353         var n = this.nodes[index];
14354         for(var i = 0, len = records.length; i < len; i++){
14355             var d = this.prepareData(records[i].data, i, records[i]);
14356             if(n){
14357                 this.tpl.insertBefore(n, d);
14358             }else{
14359                 
14360                 this.tpl.append(this.el, d);
14361             }
14362         }
14363         this.updateIndexes(index);
14364     },
14365
14366     onRemove : function(ds, record, index){
14367        // Roo.log('onRemove');
14368         this.clearSelections();
14369         var el = this.dataName  ?
14370             this.el.child('.roo-tpl-' + this.dataName) :
14371             this.el; 
14372         
14373         el.dom.removeChild(this.nodes[index]);
14374         this.updateIndexes(index);
14375     },
14376
14377     /**
14378      * Refresh an individual node.
14379      * @param {Number} index
14380      */
14381     refreshNode : function(index){
14382         this.onUpdate(this.store, this.store.getAt(index));
14383     },
14384
14385     updateIndexes : function(startIndex, endIndex){
14386         var ns = this.nodes;
14387         startIndex = startIndex || 0;
14388         endIndex = endIndex || ns.length - 1;
14389         for(var i = startIndex; i <= endIndex; i++){
14390             ns[i].nodeIndex = i;
14391         }
14392     },
14393
14394     /**
14395      * Changes the data store this view uses and refresh the view.
14396      * @param {Store} store
14397      */
14398     setStore : function(store, initial){
14399         if(!initial && this.store){
14400             this.store.un("datachanged", this.refresh);
14401             this.store.un("add", this.onAdd);
14402             this.store.un("remove", this.onRemove);
14403             this.store.un("update", this.onUpdate);
14404             this.store.un("clear", this.refresh);
14405             this.store.un("beforeload", this.onBeforeLoad);
14406             this.store.un("load", this.onLoad);
14407             this.store.un("loadexception", this.onLoad);
14408         }
14409         if(store){
14410           
14411             store.on("datachanged", this.refresh, this);
14412             store.on("add", this.onAdd, this);
14413             store.on("remove", this.onRemove, this);
14414             store.on("update", this.onUpdate, this);
14415             store.on("clear", this.refresh, this);
14416             store.on("beforeload", this.onBeforeLoad, this);
14417             store.on("load", this.onLoad, this);
14418             store.on("loadexception", this.onLoad, this);
14419         }
14420         
14421         if(store){
14422             this.refresh();
14423         }
14424     },
14425     /**
14426      * onbeforeLoad - masks the loading area.
14427      *
14428      */
14429     onBeforeLoad : function(store,opts)
14430     {
14431          //Roo.log('onBeforeLoad');   
14432         if (!opts.add) {
14433             this.el.update("");
14434         }
14435         this.el.mask(this.mask ? this.mask : "Loading" ); 
14436     },
14437     onLoad : function ()
14438     {
14439         this.el.unmask();
14440     },
14441     
14442
14443     /**
14444      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14445      * @param {HTMLElement} node
14446      * @return {HTMLElement} The template node
14447      */
14448     findItemFromChild : function(node){
14449         var el = this.dataName  ?
14450             this.el.child('.roo-tpl-' + this.dataName,true) :
14451             this.el.dom; 
14452         
14453         if(!node || node.parentNode == el){
14454                     return node;
14455             }
14456             var p = node.parentNode;
14457             while(p && p != el){
14458             if(p.parentNode == el){
14459                 return p;
14460             }
14461             p = p.parentNode;
14462         }
14463             return null;
14464     },
14465
14466     /** @ignore */
14467     onClick : function(e){
14468         var item = this.findItemFromChild(e.getTarget());
14469         if(item){
14470             var index = this.indexOf(item);
14471             if(this.onItemClick(item, index, e) !== false){
14472                 this.fireEvent("click", this, index, item, e);
14473             }
14474         }else{
14475             this.clearSelections();
14476         }
14477     },
14478
14479     /** @ignore */
14480     onContextMenu : function(e){
14481         var item = this.findItemFromChild(e.getTarget());
14482         if(item){
14483             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14484         }
14485     },
14486
14487     /** @ignore */
14488     onDblClick : function(e){
14489         var item = this.findItemFromChild(e.getTarget());
14490         if(item){
14491             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14492         }
14493     },
14494
14495     onItemClick : function(item, index, e)
14496     {
14497         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14498             return false;
14499         }
14500         if (this.toggleSelect) {
14501             var m = this.isSelected(item) ? 'unselect' : 'select';
14502             //Roo.log(m);
14503             var _t = this;
14504             _t[m](item, true, false);
14505             return true;
14506         }
14507         if(this.multiSelect || this.singleSelect){
14508             if(this.multiSelect && e.shiftKey && this.lastSelection){
14509                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14510             }else{
14511                 this.select(item, this.multiSelect && e.ctrlKey);
14512                 this.lastSelection = item;
14513             }
14514             
14515             if(!this.tickable){
14516                 e.preventDefault();
14517             }
14518             
14519         }
14520         return true;
14521     },
14522
14523     /**
14524      * Get the number of selected nodes.
14525      * @return {Number}
14526      */
14527     getSelectionCount : function(){
14528         return this.selections.length;
14529     },
14530
14531     /**
14532      * Get the currently selected nodes.
14533      * @return {Array} An array of HTMLElements
14534      */
14535     getSelectedNodes : function(){
14536         return this.selections;
14537     },
14538
14539     /**
14540      * Get the indexes of the selected nodes.
14541      * @return {Array}
14542      */
14543     getSelectedIndexes : function(){
14544         var indexes = [], s = this.selections;
14545         for(var i = 0, len = s.length; i < len; i++){
14546             indexes.push(s[i].nodeIndex);
14547         }
14548         return indexes;
14549     },
14550
14551     /**
14552      * Clear all selections
14553      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14554      */
14555     clearSelections : function(suppressEvent){
14556         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14557             this.cmp.elements = this.selections;
14558             this.cmp.removeClass(this.selectedClass);
14559             this.selections = [];
14560             if(!suppressEvent){
14561                 this.fireEvent("selectionchange", this, this.selections);
14562             }
14563         }
14564     },
14565
14566     /**
14567      * Returns true if the passed node is selected
14568      * @param {HTMLElement/Number} node The node or node index
14569      * @return {Boolean}
14570      */
14571     isSelected : function(node){
14572         var s = this.selections;
14573         if(s.length < 1){
14574             return false;
14575         }
14576         node = this.getNode(node);
14577         return s.indexOf(node) !== -1;
14578     },
14579
14580     /**
14581      * Selects nodes.
14582      * @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
14583      * @param {Boolean} keepExisting (optional) true to keep existing selections
14584      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14585      */
14586     select : function(nodeInfo, keepExisting, suppressEvent){
14587         if(nodeInfo instanceof Array){
14588             if(!keepExisting){
14589                 this.clearSelections(true);
14590             }
14591             for(var i = 0, len = nodeInfo.length; i < len; i++){
14592                 this.select(nodeInfo[i], true, true);
14593             }
14594             return;
14595         } 
14596         var node = this.getNode(nodeInfo);
14597         if(!node || this.isSelected(node)){
14598             return; // already selected.
14599         }
14600         if(!keepExisting){
14601             this.clearSelections(true);
14602         }
14603         
14604         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14605             Roo.fly(node).addClass(this.selectedClass);
14606             this.selections.push(node);
14607             if(!suppressEvent){
14608                 this.fireEvent("selectionchange", this, this.selections);
14609             }
14610         }
14611         
14612         
14613     },
14614       /**
14615      * Unselects nodes.
14616      * @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
14617      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14618      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14619      */
14620     unselect : function(nodeInfo, keepExisting, suppressEvent)
14621     {
14622         if(nodeInfo instanceof Array){
14623             Roo.each(this.selections, function(s) {
14624                 this.unselect(s, nodeInfo);
14625             }, this);
14626             return;
14627         }
14628         var node = this.getNode(nodeInfo);
14629         if(!node || !this.isSelected(node)){
14630             //Roo.log("not selected");
14631             return; // not selected.
14632         }
14633         // fireevent???
14634         var ns = [];
14635         Roo.each(this.selections, function(s) {
14636             if (s == node ) {
14637                 Roo.fly(node).removeClass(this.selectedClass);
14638
14639                 return;
14640             }
14641             ns.push(s);
14642         },this);
14643         
14644         this.selections= ns;
14645         this.fireEvent("selectionchange", this, this.selections);
14646     },
14647
14648     /**
14649      * Gets a template node.
14650      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14651      * @return {HTMLElement} The node or null if it wasn't found
14652      */
14653     getNode : function(nodeInfo){
14654         if(typeof nodeInfo == "string"){
14655             return document.getElementById(nodeInfo);
14656         }else if(typeof nodeInfo == "number"){
14657             return this.nodes[nodeInfo];
14658         }
14659         return nodeInfo;
14660     },
14661
14662     /**
14663      * Gets a range template nodes.
14664      * @param {Number} startIndex
14665      * @param {Number} endIndex
14666      * @return {Array} An array of nodes
14667      */
14668     getNodes : function(start, end){
14669         var ns = this.nodes;
14670         start = start || 0;
14671         end = typeof end == "undefined" ? ns.length - 1 : end;
14672         var nodes = [];
14673         if(start <= end){
14674             for(var i = start; i <= end; i++){
14675                 nodes.push(ns[i]);
14676             }
14677         } else{
14678             for(var i = start; i >= end; i--){
14679                 nodes.push(ns[i]);
14680             }
14681         }
14682         return nodes;
14683     },
14684
14685     /**
14686      * Finds the index of the passed node
14687      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14688      * @return {Number} The index of the node or -1
14689      */
14690     indexOf : function(node){
14691         node = this.getNode(node);
14692         if(typeof node.nodeIndex == "number"){
14693             return node.nodeIndex;
14694         }
14695         var ns = this.nodes;
14696         for(var i = 0, len = ns.length; i < len; i++){
14697             if(ns[i] == node){
14698                 return i;
14699             }
14700         }
14701         return -1;
14702     }
14703 });
14704 /*
14705  * - LGPL
14706  *
14707  * based on jquery fullcalendar
14708  * 
14709  */
14710
14711 Roo.bootstrap = Roo.bootstrap || {};
14712 /**
14713  * @class Roo.bootstrap.Calendar
14714  * @extends Roo.bootstrap.Component
14715  * Bootstrap Calendar class
14716  * @cfg {Boolean} loadMask (true|false) default false
14717  * @cfg {Object} header generate the user specific header of the calendar, default false
14718
14719  * @constructor
14720  * Create a new Container
14721  * @param {Object} config The config object
14722  */
14723
14724
14725
14726 Roo.bootstrap.Calendar = function(config){
14727     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14728      this.addEvents({
14729         /**
14730              * @event select
14731              * Fires when a date is selected
14732              * @param {DatePicker} this
14733              * @param {Date} date The selected date
14734              */
14735         'select': true,
14736         /**
14737              * @event monthchange
14738              * Fires when the displayed month changes 
14739              * @param {DatePicker} this
14740              * @param {Date} date The selected month
14741              */
14742         'monthchange': true,
14743         /**
14744              * @event evententer
14745              * Fires when mouse over an event
14746              * @param {Calendar} this
14747              * @param {event} Event
14748              */
14749         'evententer': true,
14750         /**
14751              * @event eventleave
14752              * Fires when the mouse leaves an
14753              * @param {Calendar} this
14754              * @param {event}
14755              */
14756         'eventleave': true,
14757         /**
14758              * @event eventclick
14759              * Fires when the mouse click an
14760              * @param {Calendar} this
14761              * @param {event}
14762              */
14763         'eventclick': true
14764         
14765     });
14766
14767 };
14768
14769 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14770     
14771      /**
14772      * @cfg {Number} startDay
14773      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14774      */
14775     startDay : 0,
14776     
14777     loadMask : false,
14778     
14779     header : false,
14780       
14781     getAutoCreate : function(){
14782         
14783         
14784         var fc_button = function(name, corner, style, content ) {
14785             return Roo.apply({},{
14786                 tag : 'span',
14787                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14788                          (corner.length ?
14789                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14790                             ''
14791                         ),
14792                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14793                 unselectable: 'on'
14794             });
14795         };
14796         
14797         var header = {};
14798         
14799         if(!this.header){
14800             header = {
14801                 tag : 'table',
14802                 cls : 'fc-header',
14803                 style : 'width:100%',
14804                 cn : [
14805                     {
14806                         tag: 'tr',
14807                         cn : [
14808                             {
14809                                 tag : 'td',
14810                                 cls : 'fc-header-left',
14811                                 cn : [
14812                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14813                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14814                                     { tag: 'span', cls: 'fc-header-space' },
14815                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14816
14817
14818                                 ]
14819                             },
14820
14821                             {
14822                                 tag : 'td',
14823                                 cls : 'fc-header-center',
14824                                 cn : [
14825                                     {
14826                                         tag: 'span',
14827                                         cls: 'fc-header-title',
14828                                         cn : {
14829                                             tag: 'H2',
14830                                             html : 'month / year'
14831                                         }
14832                                     }
14833
14834                                 ]
14835                             },
14836                             {
14837                                 tag : 'td',
14838                                 cls : 'fc-header-right',
14839                                 cn : [
14840                               /*      fc_button('month', 'left', '', 'month' ),
14841                                     fc_button('week', '', '', 'week' ),
14842                                     fc_button('day', 'right', '', 'day' )
14843                                 */    
14844
14845                                 ]
14846                             }
14847
14848                         ]
14849                     }
14850                 ]
14851             };
14852         }
14853         
14854         header = this.header;
14855         
14856        
14857         var cal_heads = function() {
14858             var ret = [];
14859             // fixme - handle this.
14860             
14861             for (var i =0; i < Date.dayNames.length; i++) {
14862                 var d = Date.dayNames[i];
14863                 ret.push({
14864                     tag: 'th',
14865                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14866                     html : d.substring(0,3)
14867                 });
14868                 
14869             }
14870             ret[0].cls += ' fc-first';
14871             ret[6].cls += ' fc-last';
14872             return ret;
14873         };
14874         var cal_cell = function(n) {
14875             return  {
14876                 tag: 'td',
14877                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14878                 cn : [
14879                     {
14880                         cn : [
14881                             {
14882                                 cls: 'fc-day-number',
14883                                 html: 'D'
14884                             },
14885                             {
14886                                 cls: 'fc-day-content',
14887                              
14888                                 cn : [
14889                                      {
14890                                         style: 'position: relative;' // height: 17px;
14891                                     }
14892                                 ]
14893                             }
14894                             
14895                             
14896                         ]
14897                     }
14898                 ]
14899                 
14900             }
14901         };
14902         var cal_rows = function() {
14903             
14904             var ret = [];
14905             for (var r = 0; r < 6; r++) {
14906                 var row= {
14907                     tag : 'tr',
14908                     cls : 'fc-week',
14909                     cn : []
14910                 };
14911                 
14912                 for (var i =0; i < Date.dayNames.length; i++) {
14913                     var d = Date.dayNames[i];
14914                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14915
14916                 }
14917                 row.cn[0].cls+=' fc-first';
14918                 row.cn[0].cn[0].style = 'min-height:90px';
14919                 row.cn[6].cls+=' fc-last';
14920                 ret.push(row);
14921                 
14922             }
14923             ret[0].cls += ' fc-first';
14924             ret[4].cls += ' fc-prev-last';
14925             ret[5].cls += ' fc-last';
14926             return ret;
14927             
14928         };
14929         
14930         var cal_table = {
14931             tag: 'table',
14932             cls: 'fc-border-separate',
14933             style : 'width:100%',
14934             cellspacing  : 0,
14935             cn : [
14936                 { 
14937                     tag: 'thead',
14938                     cn : [
14939                         { 
14940                             tag: 'tr',
14941                             cls : 'fc-first fc-last',
14942                             cn : cal_heads()
14943                         }
14944                     ]
14945                 },
14946                 { 
14947                     tag: 'tbody',
14948                     cn : cal_rows()
14949                 }
14950                   
14951             ]
14952         };
14953          
14954          var cfg = {
14955             cls : 'fc fc-ltr',
14956             cn : [
14957                 header,
14958                 {
14959                     cls : 'fc-content',
14960                     style : "position: relative;",
14961                     cn : [
14962                         {
14963                             cls : 'fc-view fc-view-month fc-grid',
14964                             style : 'position: relative',
14965                             unselectable : 'on',
14966                             cn : [
14967                                 {
14968                                     cls : 'fc-event-container',
14969                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14970                                 },
14971                                 cal_table
14972                             ]
14973                         }
14974                     ]
14975     
14976                 }
14977            ] 
14978             
14979         };
14980         
14981          
14982         
14983         return cfg;
14984     },
14985     
14986     
14987     initEvents : function()
14988     {
14989         if(!this.store){
14990             throw "can not find store for calendar";
14991         }
14992         
14993         var mark = {
14994             tag: "div",
14995             cls:"x-dlg-mask",
14996             style: "text-align:center",
14997             cn: [
14998                 {
14999                     tag: "div",
15000                     style: "background-color:white;width:50%;margin:250 auto",
15001                     cn: [
15002                         {
15003                             tag: "img",
15004                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15005                         },
15006                         {
15007                             tag: "span",
15008                             html: "Loading"
15009                         }
15010                         
15011                     ]
15012                 }
15013             ]
15014         };
15015         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15016         
15017         var size = this.el.select('.fc-content', true).first().getSize();
15018         this.maskEl.setSize(size.width, size.height);
15019         this.maskEl.enableDisplayMode("block");
15020         if(!this.loadMask){
15021             this.maskEl.hide();
15022         }
15023         
15024         this.store = Roo.factory(this.store, Roo.data);
15025         this.store.on('load', this.onLoad, this);
15026         this.store.on('beforeload', this.onBeforeLoad, this);
15027         
15028         this.resize();
15029         
15030         this.cells = this.el.select('.fc-day',true);
15031         //Roo.log(this.cells);
15032         this.textNodes = this.el.query('.fc-day-number');
15033         this.cells.addClassOnOver('fc-state-hover');
15034         
15035         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15036         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15037         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15038         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15039         
15040         this.on('monthchange', this.onMonthChange, this);
15041         
15042         this.update(new Date().clearTime());
15043     },
15044     
15045     resize : function() {
15046         var sz  = this.el.getSize();
15047         
15048         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15049         this.el.select('.fc-day-content div',true).setHeight(34);
15050     },
15051     
15052     
15053     // private
15054     showPrevMonth : function(e){
15055         this.update(this.activeDate.add("mo", -1));
15056     },
15057     showToday : function(e){
15058         this.update(new Date().clearTime());
15059     },
15060     // private
15061     showNextMonth : function(e){
15062         this.update(this.activeDate.add("mo", 1));
15063     },
15064
15065     // private
15066     showPrevYear : function(){
15067         this.update(this.activeDate.add("y", -1));
15068     },
15069
15070     // private
15071     showNextYear : function(){
15072         this.update(this.activeDate.add("y", 1));
15073     },
15074
15075     
15076    // private
15077     update : function(date)
15078     {
15079         var vd = this.activeDate;
15080         this.activeDate = date;
15081 //        if(vd && this.el){
15082 //            var t = date.getTime();
15083 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15084 //                Roo.log('using add remove');
15085 //                
15086 //                this.fireEvent('monthchange', this, date);
15087 //                
15088 //                this.cells.removeClass("fc-state-highlight");
15089 //                this.cells.each(function(c){
15090 //                   if(c.dateValue == t){
15091 //                       c.addClass("fc-state-highlight");
15092 //                       setTimeout(function(){
15093 //                            try{c.dom.firstChild.focus();}catch(e){}
15094 //                       }, 50);
15095 //                       return false;
15096 //                   }
15097 //                   return true;
15098 //                });
15099 //                return;
15100 //            }
15101 //        }
15102         
15103         var days = date.getDaysInMonth();
15104         
15105         var firstOfMonth = date.getFirstDateOfMonth();
15106         var startingPos = firstOfMonth.getDay()-this.startDay;
15107         
15108         if(startingPos < this.startDay){
15109             startingPos += 7;
15110         }
15111         
15112         var pm = date.add(Date.MONTH, -1);
15113         var prevStart = pm.getDaysInMonth()-startingPos;
15114 //        
15115         this.cells = this.el.select('.fc-day',true);
15116         this.textNodes = this.el.query('.fc-day-number');
15117         this.cells.addClassOnOver('fc-state-hover');
15118         
15119         var cells = this.cells.elements;
15120         var textEls = this.textNodes;
15121         
15122         Roo.each(cells, function(cell){
15123             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15124         });
15125         
15126         days += startingPos;
15127
15128         // convert everything to numbers so it's fast
15129         var day = 86400000;
15130         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15131         //Roo.log(d);
15132         //Roo.log(pm);
15133         //Roo.log(prevStart);
15134         
15135         var today = new Date().clearTime().getTime();
15136         var sel = date.clearTime().getTime();
15137         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15138         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15139         var ddMatch = this.disabledDatesRE;
15140         var ddText = this.disabledDatesText;
15141         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15142         var ddaysText = this.disabledDaysText;
15143         var format = this.format;
15144         
15145         var setCellClass = function(cal, cell){
15146             cell.row = 0;
15147             cell.events = [];
15148             cell.more = [];
15149             //Roo.log('set Cell Class');
15150             cell.title = "";
15151             var t = d.getTime();
15152             
15153             //Roo.log(d);
15154             
15155             cell.dateValue = t;
15156             if(t == today){
15157                 cell.className += " fc-today";
15158                 cell.className += " fc-state-highlight";
15159                 cell.title = cal.todayText;
15160             }
15161             if(t == sel){
15162                 // disable highlight in other month..
15163                 //cell.className += " fc-state-highlight";
15164                 
15165             }
15166             // disabling
15167             if(t < min) {
15168                 cell.className = " fc-state-disabled";
15169                 cell.title = cal.minText;
15170                 return;
15171             }
15172             if(t > max) {
15173                 cell.className = " fc-state-disabled";
15174                 cell.title = cal.maxText;
15175                 return;
15176             }
15177             if(ddays){
15178                 if(ddays.indexOf(d.getDay()) != -1){
15179                     cell.title = ddaysText;
15180                     cell.className = " fc-state-disabled";
15181                 }
15182             }
15183             if(ddMatch && format){
15184                 var fvalue = d.dateFormat(format);
15185                 if(ddMatch.test(fvalue)){
15186                     cell.title = ddText.replace("%0", fvalue);
15187                     cell.className = " fc-state-disabled";
15188                 }
15189             }
15190             
15191             if (!cell.initialClassName) {
15192                 cell.initialClassName = cell.dom.className;
15193             }
15194             
15195             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15196         };
15197
15198         var i = 0;
15199         
15200         for(; i < startingPos; i++) {
15201             textEls[i].innerHTML = (++prevStart);
15202             d.setDate(d.getDate()+1);
15203             
15204             cells[i].className = "fc-past fc-other-month";
15205             setCellClass(this, cells[i]);
15206         }
15207         
15208         var intDay = 0;
15209         
15210         for(; i < days; i++){
15211             intDay = i - startingPos + 1;
15212             textEls[i].innerHTML = (intDay);
15213             d.setDate(d.getDate()+1);
15214             
15215             cells[i].className = ''; // "x-date-active";
15216             setCellClass(this, cells[i]);
15217         }
15218         var extraDays = 0;
15219         
15220         for(; i < 42; i++) {
15221             textEls[i].innerHTML = (++extraDays);
15222             d.setDate(d.getDate()+1);
15223             
15224             cells[i].className = "fc-future fc-other-month";
15225             setCellClass(this, cells[i]);
15226         }
15227         
15228         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15229         
15230         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15231         
15232         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15233         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15234         
15235         if(totalRows != 6){
15236             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15237             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15238         }
15239         
15240         this.fireEvent('monthchange', this, date);
15241         
15242         
15243         /*
15244         if(!this.internalRender){
15245             var main = this.el.dom.firstChild;
15246             var w = main.offsetWidth;
15247             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15248             Roo.fly(main).setWidth(w);
15249             this.internalRender = true;
15250             // opera does not respect the auto grow header center column
15251             // then, after it gets a width opera refuses to recalculate
15252             // without a second pass
15253             if(Roo.isOpera && !this.secondPass){
15254                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15255                 this.secondPass = true;
15256                 this.update.defer(10, this, [date]);
15257             }
15258         }
15259         */
15260         
15261     },
15262     
15263     findCell : function(dt) {
15264         dt = dt.clearTime().getTime();
15265         var ret = false;
15266         this.cells.each(function(c){
15267             //Roo.log("check " +c.dateValue + '?=' + dt);
15268             if(c.dateValue == dt){
15269                 ret = c;
15270                 return false;
15271             }
15272             return true;
15273         });
15274         
15275         return ret;
15276     },
15277     
15278     findCells : function(ev) {
15279         var s = ev.start.clone().clearTime().getTime();
15280        // Roo.log(s);
15281         var e= ev.end.clone().clearTime().getTime();
15282        // Roo.log(e);
15283         var ret = [];
15284         this.cells.each(function(c){
15285              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15286             
15287             if(c.dateValue > e){
15288                 return ;
15289             }
15290             if(c.dateValue < s){
15291                 return ;
15292             }
15293             ret.push(c);
15294         });
15295         
15296         return ret;    
15297     },
15298     
15299 //    findBestRow: function(cells)
15300 //    {
15301 //        var ret = 0;
15302 //        
15303 //        for (var i =0 ; i < cells.length;i++) {
15304 //            ret  = Math.max(cells[i].rows || 0,ret);
15305 //        }
15306 //        return ret;
15307 //        
15308 //    },
15309     
15310     
15311     addItem : function(ev)
15312     {
15313         // look for vertical location slot in
15314         var cells = this.findCells(ev);
15315         
15316 //        ev.row = this.findBestRow(cells);
15317         
15318         // work out the location.
15319         
15320         var crow = false;
15321         var rows = [];
15322         for(var i =0; i < cells.length; i++) {
15323             
15324             cells[i].row = cells[0].row;
15325             
15326             if(i == 0){
15327                 cells[i].row = cells[i].row + 1;
15328             }
15329             
15330             if (!crow) {
15331                 crow = {
15332                     start : cells[i],
15333                     end :  cells[i]
15334                 };
15335                 continue;
15336             }
15337             if (crow.start.getY() == cells[i].getY()) {
15338                 // on same row.
15339                 crow.end = cells[i];
15340                 continue;
15341             }
15342             // different row.
15343             rows.push(crow);
15344             crow = {
15345                 start: cells[i],
15346                 end : cells[i]
15347             };
15348             
15349         }
15350         
15351         rows.push(crow);
15352         ev.els = [];
15353         ev.rows = rows;
15354         ev.cells = cells;
15355         
15356         cells[0].events.push(ev);
15357         
15358         this.calevents.push(ev);
15359     },
15360     
15361     clearEvents: function() {
15362         
15363         if(!this.calevents){
15364             return;
15365         }
15366         
15367         Roo.each(this.cells.elements, function(c){
15368             c.row = 0;
15369             c.events = [];
15370             c.more = [];
15371         });
15372         
15373         Roo.each(this.calevents, function(e) {
15374             Roo.each(e.els, function(el) {
15375                 el.un('mouseenter' ,this.onEventEnter, this);
15376                 el.un('mouseleave' ,this.onEventLeave, this);
15377                 el.remove();
15378             },this);
15379         },this);
15380         
15381         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15382             e.remove();
15383         });
15384         
15385     },
15386     
15387     renderEvents: function()
15388     {   
15389         var _this = this;
15390         
15391         this.cells.each(function(c) {
15392             
15393             if(c.row < 5){
15394                 return;
15395             }
15396             
15397             var ev = c.events;
15398             
15399             var r = 4;
15400             if(c.row != c.events.length){
15401                 r = 4 - (4 - (c.row - c.events.length));
15402             }
15403             
15404             c.events = ev.slice(0, r);
15405             c.more = ev.slice(r);
15406             
15407             if(c.more.length && c.more.length == 1){
15408                 c.events.push(c.more.pop());
15409             }
15410             
15411             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15412             
15413         });
15414             
15415         this.cells.each(function(c) {
15416             
15417             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15418             
15419             
15420             for (var e = 0; e < c.events.length; e++){
15421                 var ev = c.events[e];
15422                 var rows = ev.rows;
15423                 
15424                 for(var i = 0; i < rows.length; i++) {
15425                 
15426                     // how many rows should it span..
15427
15428                     var  cfg = {
15429                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15430                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15431
15432                         unselectable : "on",
15433                         cn : [
15434                             {
15435                                 cls: 'fc-event-inner',
15436                                 cn : [
15437     //                                {
15438     //                                  tag:'span',
15439     //                                  cls: 'fc-event-time',
15440     //                                  html : cells.length > 1 ? '' : ev.time
15441     //                                },
15442                                     {
15443                                       tag:'span',
15444                                       cls: 'fc-event-title',
15445                                       html : String.format('{0}', ev.title)
15446                                     }
15447
15448
15449                                 ]
15450                             },
15451                             {
15452                                 cls: 'ui-resizable-handle ui-resizable-e',
15453                                 html : '&nbsp;&nbsp;&nbsp'
15454                             }
15455
15456                         ]
15457                     };
15458
15459                     if (i == 0) {
15460                         cfg.cls += ' fc-event-start';
15461                     }
15462                     if ((i+1) == rows.length) {
15463                         cfg.cls += ' fc-event-end';
15464                     }
15465
15466                     var ctr = _this.el.select('.fc-event-container',true).first();
15467                     var cg = ctr.createChild(cfg);
15468
15469                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15470                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15471
15472                     var r = (c.more.length) ? 1 : 0;
15473                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15474                     cg.setWidth(ebox.right - sbox.x -2);
15475
15476                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15477                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15478                     cg.on('click', _this.onEventClick, _this, ev);
15479
15480                     ev.els.push(cg);
15481                     
15482                 }
15483                 
15484             }
15485             
15486             
15487             if(c.more.length){
15488                 var  cfg = {
15489                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15490                     style : 'position: absolute',
15491                     unselectable : "on",
15492                     cn : [
15493                         {
15494                             cls: 'fc-event-inner',
15495                             cn : [
15496                                 {
15497                                   tag:'span',
15498                                   cls: 'fc-event-title',
15499                                   html : 'More'
15500                                 }
15501
15502
15503                             ]
15504                         },
15505                         {
15506                             cls: 'ui-resizable-handle ui-resizable-e',
15507                             html : '&nbsp;&nbsp;&nbsp'
15508                         }
15509
15510                     ]
15511                 };
15512
15513                 var ctr = _this.el.select('.fc-event-container',true).first();
15514                 var cg = ctr.createChild(cfg);
15515
15516                 var sbox = c.select('.fc-day-content',true).first().getBox();
15517                 var ebox = c.select('.fc-day-content',true).first().getBox();
15518                 //Roo.log(cg);
15519                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15520                 cg.setWidth(ebox.right - sbox.x -2);
15521
15522                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15523                 
15524             }
15525             
15526         });
15527         
15528         
15529         
15530     },
15531     
15532     onEventEnter: function (e, el,event,d) {
15533         this.fireEvent('evententer', this, el, event);
15534     },
15535     
15536     onEventLeave: function (e, el,event,d) {
15537         this.fireEvent('eventleave', this, el, event);
15538     },
15539     
15540     onEventClick: function (e, el,event,d) {
15541         this.fireEvent('eventclick', this, el, event);
15542     },
15543     
15544     onMonthChange: function () {
15545         this.store.load();
15546     },
15547     
15548     onMoreEventClick: function(e, el, more)
15549     {
15550         var _this = this;
15551         
15552         this.calpopover.placement = 'right';
15553         this.calpopover.setTitle('More');
15554         
15555         this.calpopover.setContent('');
15556         
15557         var ctr = this.calpopover.el.select('.popover-content', true).first();
15558         
15559         Roo.each(more, function(m){
15560             var cfg = {
15561                 cls : 'fc-event-hori fc-event-draggable',
15562                 html : m.title
15563             };
15564             var cg = ctr.createChild(cfg);
15565             
15566             cg.on('click', _this.onEventClick, _this, m);
15567         });
15568         
15569         this.calpopover.show(el);
15570         
15571         
15572     },
15573     
15574     onLoad: function () 
15575     {   
15576         this.calevents = [];
15577         var cal = this;
15578         
15579         if(this.store.getCount() > 0){
15580             this.store.data.each(function(d){
15581                cal.addItem({
15582                     id : d.data.id,
15583                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15584                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15585                     time : d.data.start_time,
15586                     title : d.data.title,
15587                     description : d.data.description,
15588                     venue : d.data.venue
15589                 });
15590             });
15591         }
15592         
15593         this.renderEvents();
15594         
15595         if(this.calevents.length && this.loadMask){
15596             this.maskEl.hide();
15597         }
15598     },
15599     
15600     onBeforeLoad: function()
15601     {
15602         this.clearEvents();
15603         if(this.loadMask){
15604             this.maskEl.show();
15605         }
15606     }
15607 });
15608
15609  
15610  /*
15611  * - LGPL
15612  *
15613  * element
15614  * 
15615  */
15616
15617 /**
15618  * @class Roo.bootstrap.Popover
15619  * @extends Roo.bootstrap.Component
15620  * Bootstrap Popover class
15621  * @cfg {String} html contents of the popover   (or false to use children..)
15622  * @cfg {String} title of popover (or false to hide)
15623  * @cfg {String} placement how it is placed
15624  * @cfg {String} trigger click || hover (or false to trigger manually)
15625  * @cfg {String} over what (parent or false to trigger manually.)
15626  * @cfg {Number} delay - delay before showing
15627  
15628  * @constructor
15629  * Create a new Popover
15630  * @param {Object} config The config object
15631  */
15632
15633 Roo.bootstrap.Popover = function(config){
15634     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15635 };
15636
15637 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15638     
15639     title: 'Fill in a title',
15640     html: false,
15641     
15642     placement : 'right',
15643     trigger : 'hover', // hover
15644     
15645     delay : 0,
15646     
15647     over: 'parent',
15648     
15649     can_build_overlaid : false,
15650     
15651     getChildContainer : function()
15652     {
15653         return this.el.select('.popover-content',true).first();
15654     },
15655     
15656     getAutoCreate : function(){
15657          Roo.log('make popover?');
15658         var cfg = {
15659            cls : 'popover roo-dynamic',
15660            style: 'display:block',
15661            cn : [
15662                 {
15663                     cls : 'arrow'
15664                 },
15665                 {
15666                     cls : 'popover-inner',
15667                     cn : [
15668                         {
15669                             tag: 'h3',
15670                             cls: 'popover-title',
15671                             html : this.title
15672                         },
15673                         {
15674                             cls : 'popover-content',
15675                             html : this.html
15676                         }
15677                     ]
15678                     
15679                 }
15680            ]
15681         };
15682         
15683         return cfg;
15684     },
15685     setTitle: function(str)
15686     {
15687         this.title = str;
15688         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15689     },
15690     setContent: function(str)
15691     {
15692         this.html = str;
15693         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15694     },
15695     // as it get's added to the bottom of the page.
15696     onRender : function(ct, position)
15697     {
15698         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15699         if(!this.el){
15700             var cfg = Roo.apply({},  this.getAutoCreate());
15701             cfg.id = Roo.id();
15702             
15703             if (this.cls) {
15704                 cfg.cls += ' ' + this.cls;
15705             }
15706             if (this.style) {
15707                 cfg.style = this.style;
15708             }
15709             //Roo.log("adding to ");
15710             this.el = Roo.get(document.body).createChild(cfg, position);
15711             Roo.log(this.el);
15712         }
15713         this.initEvents();
15714     },
15715     
15716     initEvents : function()
15717     {
15718         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15719         this.el.enableDisplayMode('block');
15720         this.el.hide();
15721         if (this.over === false) {
15722             return; 
15723         }
15724         if (this.triggers === false) {
15725             return;
15726         }
15727         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15728         var triggers = this.trigger ? this.trigger.split(' ') : [];
15729         Roo.each(triggers, function(trigger) {
15730         
15731             if (trigger == 'click') {
15732                 on_el.on('click', this.toggle, this);
15733             } else if (trigger != 'manual') {
15734                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15735                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15736       
15737                 on_el.on(eventIn  ,this.enter, this);
15738                 on_el.on(eventOut, this.leave, this);
15739             }
15740         }, this);
15741         
15742     },
15743     
15744     
15745     // private
15746     timeout : null,
15747     hoverState : null,
15748     
15749     toggle : function () {
15750         this.hoverState == 'in' ? this.leave() : this.enter();
15751     },
15752     
15753     enter : function () {
15754        
15755     
15756         clearTimeout(this.timeout);
15757     
15758         this.hoverState = 'in';
15759     
15760         if (!this.delay || !this.delay.show) {
15761             this.show();
15762             return;
15763         }
15764         var _t = this;
15765         this.timeout = setTimeout(function () {
15766             if (_t.hoverState == 'in') {
15767                 _t.show();
15768             }
15769         }, this.delay.show)
15770     },
15771     leave : function() {
15772         clearTimeout(this.timeout);
15773     
15774         this.hoverState = 'out';
15775     
15776         if (!this.delay || !this.delay.hide) {
15777             this.hide();
15778             return;
15779         }
15780         var _t = this;
15781         this.timeout = setTimeout(function () {
15782             if (_t.hoverState == 'out') {
15783                 _t.hide();
15784             }
15785         }, this.delay.hide)
15786     },
15787     
15788     show : function (on_el)
15789     {
15790         if (!on_el) {
15791             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15792         }
15793         // set content.
15794         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15795         if (this.html !== false) {
15796             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15797         }
15798         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15799         if (!this.title.length) {
15800             this.el.select('.popover-title',true).hide();
15801         }
15802         
15803         var placement = typeof this.placement == 'function' ?
15804             this.placement.call(this, this.el, on_el) :
15805             this.placement;
15806             
15807         var autoToken = /\s?auto?\s?/i;
15808         var autoPlace = autoToken.test(placement);
15809         if (autoPlace) {
15810             placement = placement.replace(autoToken, '') || 'top';
15811         }
15812         
15813         //this.el.detach()
15814         //this.el.setXY([0,0]);
15815         this.el.show();
15816         this.el.dom.style.display='block';
15817         this.el.addClass(placement);
15818         
15819         //this.el.appendTo(on_el);
15820         
15821         var p = this.getPosition();
15822         var box = this.el.getBox();
15823         
15824         if (autoPlace) {
15825             // fixme..
15826         }
15827         var align = Roo.bootstrap.Popover.alignment[placement];
15828         this.el.alignTo(on_el, align[0],align[1]);
15829         //var arrow = this.el.select('.arrow',true).first();
15830         //arrow.set(align[2], 
15831         
15832         this.el.addClass('in');
15833         
15834         
15835         if (this.el.hasClass('fade')) {
15836             // fade it?
15837         }
15838         
15839     },
15840     hide : function()
15841     {
15842         this.el.setXY([0,0]);
15843         this.el.removeClass('in');
15844         this.el.hide();
15845         this.hoverState = null;
15846         
15847     }
15848     
15849 });
15850
15851 Roo.bootstrap.Popover.alignment = {
15852     'left' : ['r-l', [-10,0], 'right'],
15853     'right' : ['l-r', [10,0], 'left'],
15854     'bottom' : ['t-b', [0,10], 'top'],
15855     'top' : [ 'b-t', [0,-10], 'bottom']
15856 };
15857
15858  /*
15859  * - LGPL
15860  *
15861  * Progress
15862  * 
15863  */
15864
15865 /**
15866  * @class Roo.bootstrap.Progress
15867  * @extends Roo.bootstrap.Component
15868  * Bootstrap Progress class
15869  * @cfg {Boolean} striped striped of the progress bar
15870  * @cfg {Boolean} active animated of the progress bar
15871  * 
15872  * 
15873  * @constructor
15874  * Create a new Progress
15875  * @param {Object} config The config object
15876  */
15877
15878 Roo.bootstrap.Progress = function(config){
15879     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15880 };
15881
15882 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15883     
15884     striped : false,
15885     active: false,
15886     
15887     getAutoCreate : function(){
15888         var cfg = {
15889             tag: 'div',
15890             cls: 'progress'
15891         };
15892         
15893         
15894         if(this.striped){
15895             cfg.cls += ' progress-striped';
15896         }
15897       
15898         if(this.active){
15899             cfg.cls += ' active';
15900         }
15901         
15902         
15903         return cfg;
15904     }
15905    
15906 });
15907
15908  
15909
15910  /*
15911  * - LGPL
15912  *
15913  * ProgressBar
15914  * 
15915  */
15916
15917 /**
15918  * @class Roo.bootstrap.ProgressBar
15919  * @extends Roo.bootstrap.Component
15920  * Bootstrap ProgressBar class
15921  * @cfg {Number} aria_valuenow aria-value now
15922  * @cfg {Number} aria_valuemin aria-value min
15923  * @cfg {Number} aria_valuemax aria-value max
15924  * @cfg {String} label label for the progress bar
15925  * @cfg {String} panel (success | info | warning | danger )
15926  * @cfg {String} role role of the progress bar
15927  * @cfg {String} sr_only text
15928  * 
15929  * 
15930  * @constructor
15931  * Create a new ProgressBar
15932  * @param {Object} config The config object
15933  */
15934
15935 Roo.bootstrap.ProgressBar = function(config){
15936     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15937 };
15938
15939 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15940     
15941     aria_valuenow : 0,
15942     aria_valuemin : 0,
15943     aria_valuemax : 100,
15944     label : false,
15945     panel : false,
15946     role : false,
15947     sr_only: false,
15948     
15949     getAutoCreate : function()
15950     {
15951         
15952         var cfg = {
15953             tag: 'div',
15954             cls: 'progress-bar',
15955             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15956         };
15957         
15958         if(this.sr_only){
15959             cfg.cn = {
15960                 tag: 'span',
15961                 cls: 'sr-only',
15962                 html: this.sr_only
15963             }
15964         }
15965         
15966         if(this.role){
15967             cfg.role = this.role;
15968         }
15969         
15970         if(this.aria_valuenow){
15971             cfg['aria-valuenow'] = this.aria_valuenow;
15972         }
15973         
15974         if(this.aria_valuemin){
15975             cfg['aria-valuemin'] = this.aria_valuemin;
15976         }
15977         
15978         if(this.aria_valuemax){
15979             cfg['aria-valuemax'] = this.aria_valuemax;
15980         }
15981         
15982         if(this.label && !this.sr_only){
15983             cfg.html = this.label;
15984         }
15985         
15986         if(this.panel){
15987             cfg.cls += ' progress-bar-' + this.panel;
15988         }
15989         
15990         return cfg;
15991     },
15992     
15993     update : function(aria_valuenow)
15994     {
15995         this.aria_valuenow = aria_valuenow;
15996         
15997         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15998     }
15999    
16000 });
16001
16002  
16003
16004  /*
16005  * - LGPL
16006  *
16007  * column
16008  * 
16009  */
16010
16011 /**
16012  * @class Roo.bootstrap.TabGroup
16013  * @extends Roo.bootstrap.Column
16014  * Bootstrap Column class
16015  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16016  * @cfg {Boolean} carousel true to make the group behave like a carousel
16017  * @cfg {Boolean} bullets show bullets for the panels
16018  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16019  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16020  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16021  * 
16022  * @constructor
16023  * Create a new TabGroup
16024  * @param {Object} config The config object
16025  */
16026
16027 Roo.bootstrap.TabGroup = function(config){
16028     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16029     if (!this.navId) {
16030         this.navId = Roo.id();
16031     }
16032     this.tabs = [];
16033     Roo.bootstrap.TabGroup.register(this);
16034     
16035 };
16036
16037 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16038     
16039     carousel : false,
16040     transition : false,
16041     bullets : 0,
16042     timer : 0,
16043     autoslide : false,
16044     slideFn : false,
16045     slideOnTouch : false,
16046     
16047     getAutoCreate : function()
16048     {
16049         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16050         
16051         cfg.cls += ' tab-content';
16052         
16053         Roo.log('get auto create...............');
16054         
16055         if (this.carousel) {
16056             cfg.cls += ' carousel slide';
16057             
16058             cfg.cn = [{
16059                cls : 'carousel-inner'
16060             }];
16061         
16062             if(this.bullets  && !Roo.isTouch){
16063                 
16064                 var bullets = {
16065                     cls : 'carousel-bullets',
16066                     cn : []
16067                 };
16068                
16069                 if(this.bullets_cls){
16070                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16071                 }
16072                  /*
16073                 for (var i = 0; i < this.bullets; i++){
16074                     bullets.cn.push({
16075                         cls : 'bullet bullet-' + i
16076                     });
16077                 }
16078                 */
16079                 bullets.cn.push({
16080                     cls : 'clear'
16081                 });
16082                 
16083                 cfg.cn[0].cn = bullets;
16084             }
16085         }
16086         
16087         return cfg;
16088     },
16089     
16090     initEvents:  function()
16091     {
16092         Roo.log('-------- init events on tab group ---------');
16093         
16094         
16095         
16096         Roo.log(this);
16097         
16098         if(Roo.isTouch && this.slideOnTouch){
16099             this.el.on("touchstart", this.onTouchStart, this);
16100         }
16101         
16102         if(this.autoslide){
16103             var _this = this;
16104             
16105             this.slideFn = window.setInterval(function() {
16106                 _this.showPanelNext();
16107             }, this.timer);
16108         }
16109         
16110     },
16111     
16112     onTouchStart : function(e, el, o)
16113     {
16114         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16115             return;
16116         }
16117         
16118         this.showPanelNext();
16119     },
16120     
16121     getChildContainer : function()
16122     {
16123         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16124     },
16125     
16126     /**
16127     * register a Navigation item
16128     * @param {Roo.bootstrap.NavItem} the navitem to add
16129     */
16130     register : function(item)
16131     {
16132         this.tabs.push( item);
16133         item.navId = this.navId; // not really needed..
16134         this.addBullet();
16135     
16136     },
16137     
16138     getActivePanel : function()
16139     {
16140         var r = false;
16141         Roo.each(this.tabs, function(t) {
16142             if (t.active) {
16143                 r = t;
16144                 return false;
16145             }
16146             return null;
16147         });
16148         return r;
16149         
16150     },
16151     getPanelByName : function(n)
16152     {
16153         var r = false;
16154         Roo.each(this.tabs, function(t) {
16155             if (t.tabId == n) {
16156                 r = t;
16157                 return false;
16158             }
16159             return null;
16160         });
16161         return r;
16162     },
16163     indexOfPanel : function(p)
16164     {
16165         var r = false;
16166         Roo.each(this.tabs, function(t,i) {
16167             if (t.tabId == p.tabId) {
16168                 r = i;
16169                 return false;
16170             }
16171             return null;
16172         });
16173         return r;
16174     },
16175     /**
16176      * show a specific panel
16177      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16178      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16179      */
16180     showPanel : function (pan)
16181     {
16182         if(this.transition){
16183             Roo.log("waiting for the transitionend");
16184             return;
16185         }
16186         
16187         if (typeof(pan) == 'number') {
16188             pan = this.tabs[pan];
16189         }
16190         if (typeof(pan) == 'string') {
16191             pan = this.getPanelByName(pan);
16192         }
16193         if (pan.tabId == this.getActivePanel().tabId) {
16194             return true;
16195         }
16196         var cur = this.getActivePanel();
16197         
16198         if (false === cur.fireEvent('beforedeactivate')) {
16199             return false;
16200         }
16201         
16202         if(this.bullets > 0 && !Roo.isTouch){
16203             this.setActiveBullet(this.indexOfPanel(pan));
16204         }
16205         
16206         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16207             
16208             this.transition = true;
16209             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16210             var lr = dir == 'next' ? 'left' : 'right';
16211             pan.el.addClass(dir); // or prev
16212             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16213             cur.el.addClass(lr); // or right
16214             pan.el.addClass(lr);
16215             
16216             var _this = this;
16217             cur.el.on('transitionend', function() {
16218                 Roo.log("trans end?");
16219                 
16220                 pan.el.removeClass([lr,dir]);
16221                 pan.setActive(true);
16222                 
16223                 cur.el.removeClass([lr]);
16224                 cur.setActive(false);
16225                 
16226                 _this.transition = false;
16227                 
16228             }, this, { single:  true } );
16229             
16230             return true;
16231         }
16232         
16233         cur.setActive(false);
16234         pan.setActive(true);
16235         
16236         return true;
16237         
16238     },
16239     showPanelNext : function()
16240     {
16241         var i = this.indexOfPanel(this.getActivePanel());
16242         
16243         if (i >= this.tabs.length - 1 && !this.autoslide) {
16244             return;
16245         }
16246         
16247         if (i >= this.tabs.length - 1 && this.autoslide) {
16248             i = -1;
16249         }
16250         
16251         this.showPanel(this.tabs[i+1]);
16252     },
16253     
16254     showPanelPrev : function()
16255     {
16256         var i = this.indexOfPanel(this.getActivePanel());
16257         
16258         if (i  < 1 && !this.autoslide) {
16259             return;
16260         }
16261         
16262         if (i < 1 && this.autoslide) {
16263             i = this.tabs.length;
16264         }
16265         
16266         this.showPanel(this.tabs[i-1]);
16267     },
16268     
16269     
16270     addBullet: function()
16271     {
16272         if(!this.bullets || Roo.isTouch){
16273             return;
16274         }
16275         var ctr = this.el.select('.carousel-bullets',true).first();
16276         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16277         var bullet = ctr.createChild({
16278             cls : 'bullet bullet-' + i
16279         },ctr.dom.lastChild);
16280         
16281         
16282         var _this = this;
16283         
16284         bullet.on('click', (function(e, el, o, ii, t){
16285
16286             e.preventDefault();
16287
16288             this.showPanel(ii);
16289
16290             if(this.autoslide && this.slideFn){
16291                 clearInterval(this.slideFn);
16292                 this.slideFn = window.setInterval(function() {
16293                     _this.showPanelNext();
16294                 }, this.timer);
16295             }
16296
16297         }).createDelegate(this, [i, bullet], true));
16298                 
16299         
16300     },
16301      
16302     setActiveBullet : function(i)
16303     {
16304         if(Roo.isTouch){
16305             return;
16306         }
16307         
16308         Roo.each(this.el.select('.bullet', true).elements, function(el){
16309             el.removeClass('selected');
16310         });
16311
16312         var bullet = this.el.select('.bullet-' + i, true).first();
16313         
16314         if(!bullet){
16315             return;
16316         }
16317         
16318         bullet.addClass('selected');
16319     }
16320     
16321     
16322   
16323 });
16324
16325  
16326
16327  
16328  
16329 Roo.apply(Roo.bootstrap.TabGroup, {
16330     
16331     groups: {},
16332      /**
16333     * register a Navigation Group
16334     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16335     */
16336     register : function(navgrp)
16337     {
16338         this.groups[navgrp.navId] = navgrp;
16339         
16340     },
16341     /**
16342     * fetch a Navigation Group based on the navigation ID
16343     * if one does not exist , it will get created.
16344     * @param {string} the navgroup to add
16345     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16346     */
16347     get: function(navId) {
16348         if (typeof(this.groups[navId]) == 'undefined') {
16349             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16350         }
16351         return this.groups[navId] ;
16352     }
16353     
16354     
16355     
16356 });
16357
16358  /*
16359  * - LGPL
16360  *
16361  * TabPanel
16362  * 
16363  */
16364
16365 /**
16366  * @class Roo.bootstrap.TabPanel
16367  * @extends Roo.bootstrap.Component
16368  * Bootstrap TabPanel class
16369  * @cfg {Boolean} active panel active
16370  * @cfg {String} html panel content
16371  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16372  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16373  * 
16374  * 
16375  * @constructor
16376  * Create a new TabPanel
16377  * @param {Object} config The config object
16378  */
16379
16380 Roo.bootstrap.TabPanel = function(config){
16381     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16382     this.addEvents({
16383         /**
16384              * @event changed
16385              * Fires when the active status changes
16386              * @param {Roo.bootstrap.TabPanel} this
16387              * @param {Boolean} state the new state
16388             
16389          */
16390         'changed': true,
16391         /**
16392              * @event beforedeactivate
16393              * Fires before a tab is de-activated - can be used to do validation on a form.
16394              * @param {Roo.bootstrap.TabPanel} this
16395              * @return {Boolean} false if there is an error
16396             
16397          */
16398         'beforedeactivate': true
16399      });
16400     
16401     this.tabId = this.tabId || Roo.id();
16402   
16403 };
16404
16405 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16406     
16407     active: false,
16408     html: false,
16409     tabId: false,
16410     navId : false,
16411     
16412     getAutoCreate : function(){
16413         var cfg = {
16414             tag: 'div',
16415             // item is needed for carousel - not sure if it has any effect otherwise
16416             cls: 'tab-pane item',
16417             html: this.html || ''
16418         };
16419         
16420         if(this.active){
16421             cfg.cls += ' active';
16422         }
16423         
16424         if(this.tabId){
16425             cfg.tabId = this.tabId;
16426         }
16427         
16428         
16429         return cfg;
16430     },
16431     
16432     initEvents:  function()
16433     {
16434         Roo.log('-------- init events on tab panel ---------');
16435         
16436         var p = this.parent();
16437         this.navId = this.navId || p.navId;
16438         
16439         if (typeof(this.navId) != 'undefined') {
16440             // not really needed.. but just in case.. parent should be a NavGroup.
16441             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16442             Roo.log(['register', tg, this]);
16443             tg.register(this);
16444             
16445             var i = tg.tabs.length - 1;
16446             
16447             if(this.active && tg.bullets > 0 && i < tg.bullets){
16448                 tg.setActiveBullet(i);
16449             }
16450         }
16451         
16452     },
16453     
16454     
16455     onRender : function(ct, position)
16456     {
16457        // Roo.log("Call onRender: " + this.xtype);
16458         
16459         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16460         
16461         
16462         
16463         
16464         
16465     },
16466     
16467     setActive: function(state)
16468     {
16469         Roo.log("panel - set active " + this.tabId + "=" + state);
16470         
16471         this.active = state;
16472         if (!state) {
16473             this.el.removeClass('active');
16474             
16475         } else  if (!this.el.hasClass('active')) {
16476             this.el.addClass('active');
16477         }
16478         
16479         this.fireEvent('changed', this, state);
16480     }
16481     
16482     
16483 });
16484  
16485
16486  
16487
16488  /*
16489  * - LGPL
16490  *
16491  * DateField
16492  * 
16493  */
16494
16495 /**
16496  * @class Roo.bootstrap.DateField
16497  * @extends Roo.bootstrap.Input
16498  * Bootstrap DateField class
16499  * @cfg {Number} weekStart default 0
16500  * @cfg {String} viewMode default empty, (months|years)
16501  * @cfg {String} minViewMode default empty, (months|years)
16502  * @cfg {Number} startDate default -Infinity
16503  * @cfg {Number} endDate default Infinity
16504  * @cfg {Boolean} todayHighlight default false
16505  * @cfg {Boolean} todayBtn default false
16506  * @cfg {Boolean} calendarWeeks default false
16507  * @cfg {Object} daysOfWeekDisabled default empty
16508  * @cfg {Boolean} singleMode default false (true | false)
16509  * 
16510  * @cfg {Boolean} keyboardNavigation default true
16511  * @cfg {String} language default en
16512  * 
16513  * @constructor
16514  * Create a new DateField
16515  * @param {Object} config The config object
16516  */
16517
16518 Roo.bootstrap.DateField = function(config){
16519     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16520      this.addEvents({
16521             /**
16522              * @event show
16523              * Fires when this field show.
16524              * @param {Roo.bootstrap.DateField} this
16525              * @param {Mixed} date The date value
16526              */
16527             show : true,
16528             /**
16529              * @event show
16530              * Fires when this field hide.
16531              * @param {Roo.bootstrap.DateField} this
16532              * @param {Mixed} date The date value
16533              */
16534             hide : true,
16535             /**
16536              * @event select
16537              * Fires when select a date.
16538              * @param {Roo.bootstrap.DateField} this
16539              * @param {Mixed} date The date value
16540              */
16541             select : true,
16542             /**
16543              * @event beforeselect
16544              * Fires when before select a date.
16545              * @param {Roo.bootstrap.DateField} this
16546              * @param {Mixed} date The date value
16547              */
16548             beforeselect : true
16549         });
16550 };
16551
16552 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16553     
16554     /**
16555      * @cfg {String} format
16556      * The default date format string which can be overriden for localization support.  The format must be
16557      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16558      */
16559     format : "m/d/y",
16560     /**
16561      * @cfg {String} altFormats
16562      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16563      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16564      */
16565     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16566     
16567     weekStart : 0,
16568     
16569     viewMode : '',
16570     
16571     minViewMode : '',
16572     
16573     todayHighlight : false,
16574     
16575     todayBtn: false,
16576     
16577     language: 'en',
16578     
16579     keyboardNavigation: true,
16580     
16581     calendarWeeks: false,
16582     
16583     startDate: -Infinity,
16584     
16585     endDate: Infinity,
16586     
16587     daysOfWeekDisabled: [],
16588     
16589     _events: [],
16590     
16591     singleMode : false,
16592     
16593     UTCDate: function()
16594     {
16595         return new Date(Date.UTC.apply(Date, arguments));
16596     },
16597     
16598     UTCToday: function()
16599     {
16600         var today = new Date();
16601         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16602     },
16603     
16604     getDate: function() {
16605             var d = this.getUTCDate();
16606             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16607     },
16608     
16609     getUTCDate: function() {
16610             return this.date;
16611     },
16612     
16613     setDate: function(d) {
16614             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16615     },
16616     
16617     setUTCDate: function(d) {
16618             this.date = d;
16619             this.setValue(this.formatDate(this.date));
16620     },
16621         
16622     onRender: function(ct, position)
16623     {
16624         
16625         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16626         
16627         this.language = this.language || 'en';
16628         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16629         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16630         
16631         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16632         this.format = this.format || 'm/d/y';
16633         this.isInline = false;
16634         this.isInput = true;
16635         this.component = this.el.select('.add-on', true).first() || false;
16636         this.component = (this.component && this.component.length === 0) ? false : this.component;
16637         this.hasInput = this.component && this.inputEL().length;
16638         
16639         if (typeof(this.minViewMode === 'string')) {
16640             switch (this.minViewMode) {
16641                 case 'months':
16642                     this.minViewMode = 1;
16643                     break;
16644                 case 'years':
16645                     this.minViewMode = 2;
16646                     break;
16647                 default:
16648                     this.minViewMode = 0;
16649                     break;
16650             }
16651         }
16652         
16653         if (typeof(this.viewMode === 'string')) {
16654             switch (this.viewMode) {
16655                 case 'months':
16656                     this.viewMode = 1;
16657                     break;
16658                 case 'years':
16659                     this.viewMode = 2;
16660                     break;
16661                 default:
16662                     this.viewMode = 0;
16663                     break;
16664             }
16665         }
16666                 
16667         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16668         
16669 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16670         
16671         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16672         
16673         this.picker().on('mousedown', this.onMousedown, this);
16674         this.picker().on('click', this.onClick, this);
16675         
16676         this.picker().addClass('datepicker-dropdown');
16677         
16678         this.startViewMode = this.viewMode;
16679         
16680         if(this.singleMode){
16681             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16682                 v.setVisibilityMode(Roo.Element.DISPLAY);
16683                 v.hide();
16684             });
16685             
16686             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16687                 v.setStyle('width', '189px');
16688             });
16689         }
16690         
16691         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16692             if(!this.calendarWeeks){
16693                 v.remove();
16694                 return;
16695             }
16696             
16697             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16698             v.attr('colspan', function(i, val){
16699                 return parseInt(val) + 1;
16700             });
16701         });
16702                         
16703         
16704         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16705         
16706         this.setStartDate(this.startDate);
16707         this.setEndDate(this.endDate);
16708         
16709         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16710         
16711         this.fillDow();
16712         this.fillMonths();
16713         this.update();
16714         this.showMode();
16715         
16716         if(this.isInline) {
16717             this.show();
16718         }
16719     },
16720     
16721     picker : function()
16722     {
16723         return this.pickerEl;
16724 //        return this.el.select('.datepicker', true).first();
16725     },
16726     
16727     fillDow: function()
16728     {
16729         var dowCnt = this.weekStart;
16730         
16731         var dow = {
16732             tag: 'tr',
16733             cn: [
16734                 
16735             ]
16736         };
16737         
16738         if(this.calendarWeeks){
16739             dow.cn.push({
16740                 tag: 'th',
16741                 cls: 'cw',
16742                 html: '&nbsp;'
16743             })
16744         }
16745         
16746         while (dowCnt < this.weekStart + 7) {
16747             dow.cn.push({
16748                 tag: 'th',
16749                 cls: 'dow',
16750                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16751             });
16752         }
16753         
16754         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16755     },
16756     
16757     fillMonths: function()
16758     {    
16759         var i = 0;
16760         var months = this.picker().select('>.datepicker-months td', true).first();
16761         
16762         months.dom.innerHTML = '';
16763         
16764         while (i < 12) {
16765             var month = {
16766                 tag: 'span',
16767                 cls: 'month',
16768                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16769             };
16770             
16771             months.createChild(month);
16772         }
16773         
16774     },
16775     
16776     update: function()
16777     {
16778         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;
16779         
16780         if (this.date < this.startDate) {
16781             this.viewDate = new Date(this.startDate);
16782         } else if (this.date > this.endDate) {
16783             this.viewDate = new Date(this.endDate);
16784         } else {
16785             this.viewDate = new Date(this.date);
16786         }
16787         
16788         this.fill();
16789     },
16790     
16791     fill: function() 
16792     {
16793         var d = new Date(this.viewDate),
16794                 year = d.getUTCFullYear(),
16795                 month = d.getUTCMonth(),
16796                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16797                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16798                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16799                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16800                 currentDate = this.date && this.date.valueOf(),
16801                 today = this.UTCToday();
16802         
16803         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16804         
16805 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16806         
16807 //        this.picker.select('>tfoot th.today').
16808 //                                              .text(dates[this.language].today)
16809 //                                              .toggle(this.todayBtn !== false);
16810     
16811         this.updateNavArrows();
16812         this.fillMonths();
16813                                                 
16814         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16815         
16816         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16817          
16818         prevMonth.setUTCDate(day);
16819         
16820         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16821         
16822         var nextMonth = new Date(prevMonth);
16823         
16824         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16825         
16826         nextMonth = nextMonth.valueOf();
16827         
16828         var fillMonths = false;
16829         
16830         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16831         
16832         while(prevMonth.valueOf() < nextMonth) {
16833             var clsName = '';
16834             
16835             if (prevMonth.getUTCDay() === this.weekStart) {
16836                 if(fillMonths){
16837                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16838                 }
16839                     
16840                 fillMonths = {
16841                     tag: 'tr',
16842                     cn: []
16843                 };
16844                 
16845                 if(this.calendarWeeks){
16846                     // ISO 8601: First week contains first thursday.
16847                     // ISO also states week starts on Monday, but we can be more abstract here.
16848                     var
16849                     // Start of current week: based on weekstart/current date
16850                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16851                     // Thursday of this week
16852                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16853                     // First Thursday of year, year from thursday
16854                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16855                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16856                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16857                     
16858                     fillMonths.cn.push({
16859                         tag: 'td',
16860                         cls: 'cw',
16861                         html: calWeek
16862                     });
16863                 }
16864             }
16865             
16866             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16867                 clsName += ' old';
16868             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16869                 clsName += ' new';
16870             }
16871             if (this.todayHighlight &&
16872                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16873                 prevMonth.getUTCMonth() == today.getMonth() &&
16874                 prevMonth.getUTCDate() == today.getDate()) {
16875                 clsName += ' today';
16876             }
16877             
16878             if (currentDate && prevMonth.valueOf() === currentDate) {
16879                 clsName += ' active';
16880             }
16881             
16882             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16883                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16884                     clsName += ' disabled';
16885             }
16886             
16887             fillMonths.cn.push({
16888                 tag: 'td',
16889                 cls: 'day ' + clsName,
16890                 html: prevMonth.getDate()
16891             });
16892             
16893             prevMonth.setDate(prevMonth.getDate()+1);
16894         }
16895           
16896         var currentYear = this.date && this.date.getUTCFullYear();
16897         var currentMonth = this.date && this.date.getUTCMonth();
16898         
16899         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16900         
16901         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16902             v.removeClass('active');
16903             
16904             if(currentYear === year && k === currentMonth){
16905                 v.addClass('active');
16906             }
16907             
16908             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16909                 v.addClass('disabled');
16910             }
16911             
16912         });
16913         
16914         
16915         year = parseInt(year/10, 10) * 10;
16916         
16917         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16918         
16919         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16920         
16921         year -= 1;
16922         for (var i = -1; i < 11; i++) {
16923             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16924                 tag: 'span',
16925                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16926                 html: year
16927             });
16928             
16929             year += 1;
16930         }
16931     },
16932     
16933     showMode: function(dir) 
16934     {
16935         if (dir) {
16936             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16937         }
16938         
16939         Roo.each(this.picker().select('>div',true).elements, function(v){
16940             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16941             v.hide();
16942         });
16943         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16944     },
16945     
16946     place: function()
16947     {
16948         if(this.isInline) {
16949             return;
16950         }
16951         
16952         this.picker().removeClass(['bottom', 'top']);
16953         
16954         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16955             /*
16956              * place to the top of element!
16957              *
16958              */
16959             
16960             this.picker().addClass('top');
16961             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16962             
16963             return;
16964         }
16965         
16966         this.picker().addClass('bottom');
16967         
16968         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16969     },
16970     
16971     parseDate : function(value)
16972     {
16973         if(!value || value instanceof Date){
16974             return value;
16975         }
16976         var v = Date.parseDate(value, this.format);
16977         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16978             v = Date.parseDate(value, 'Y-m-d');
16979         }
16980         if(!v && this.altFormats){
16981             if(!this.altFormatsArray){
16982                 this.altFormatsArray = this.altFormats.split("|");
16983             }
16984             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16985                 v = Date.parseDate(value, this.altFormatsArray[i]);
16986             }
16987         }
16988         return v;
16989     },
16990     
16991     formatDate : function(date, fmt)
16992     {   
16993         return (!date || !(date instanceof Date)) ?
16994         date : date.dateFormat(fmt || this.format);
16995     },
16996     
16997     onFocus : function()
16998     {
16999         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17000         this.show();
17001     },
17002     
17003     onBlur : function()
17004     {
17005         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17006         
17007         var d = this.inputEl().getValue();
17008         
17009         this.setValue(d);
17010                 
17011         this.hide();
17012     },
17013     
17014     show : function()
17015     {
17016         this.picker().show();
17017         this.update();
17018         this.place();
17019         
17020         this.fireEvent('show', this, this.date);
17021     },
17022     
17023     hide : function()
17024     {
17025         if(this.isInline) {
17026             return;
17027         }
17028         this.picker().hide();
17029         this.viewMode = this.startViewMode;
17030         this.showMode();
17031         
17032         this.fireEvent('hide', this, this.date);
17033         
17034     },
17035     
17036     onMousedown: function(e)
17037     {
17038         e.stopPropagation();
17039         e.preventDefault();
17040     },
17041     
17042     keyup: function(e)
17043     {
17044         Roo.bootstrap.DateField.superclass.keyup.call(this);
17045         this.update();
17046     },
17047
17048     setValue: function(v)
17049     {
17050         if(this.fireEvent('beforeselect', this, v) !== false){
17051             var d = new Date(this.parseDate(v) ).clearTime();
17052         
17053             if(isNaN(d.getTime())){
17054                 this.date = this.viewDate = '';
17055                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17056                 return;
17057             }
17058
17059             v = this.formatDate(d);
17060
17061             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17062
17063             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17064
17065             this.update();
17066
17067             this.fireEvent('select', this, this.date);
17068         }
17069     },
17070     
17071     getValue: function()
17072     {
17073         return this.formatDate(this.date);
17074     },
17075     
17076     fireKey: function(e)
17077     {
17078         if (!this.picker().isVisible()){
17079             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17080                 this.show();
17081             }
17082             return;
17083         }
17084         
17085         var dateChanged = false,
17086         dir, day, month,
17087         newDate, newViewDate;
17088         
17089         switch(e.keyCode){
17090             case 27: // escape
17091                 this.hide();
17092                 e.preventDefault();
17093                 break;
17094             case 37: // left
17095             case 39: // right
17096                 if (!this.keyboardNavigation) {
17097                     break;
17098                 }
17099                 dir = e.keyCode == 37 ? -1 : 1;
17100                 
17101                 if (e.ctrlKey){
17102                     newDate = this.moveYear(this.date, dir);
17103                     newViewDate = this.moveYear(this.viewDate, dir);
17104                 } else if (e.shiftKey){
17105                     newDate = this.moveMonth(this.date, dir);
17106                     newViewDate = this.moveMonth(this.viewDate, dir);
17107                 } else {
17108                     newDate = new Date(this.date);
17109                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17110                     newViewDate = new Date(this.viewDate);
17111                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17112                 }
17113                 if (this.dateWithinRange(newDate)){
17114                     this.date = newDate;
17115                     this.viewDate = newViewDate;
17116                     this.setValue(this.formatDate(this.date));
17117 //                    this.update();
17118                     e.preventDefault();
17119                     dateChanged = true;
17120                 }
17121                 break;
17122             case 38: // up
17123             case 40: // down
17124                 if (!this.keyboardNavigation) {
17125                     break;
17126                 }
17127                 dir = e.keyCode == 38 ? -1 : 1;
17128                 if (e.ctrlKey){
17129                     newDate = this.moveYear(this.date, dir);
17130                     newViewDate = this.moveYear(this.viewDate, dir);
17131                 } else if (e.shiftKey){
17132                     newDate = this.moveMonth(this.date, dir);
17133                     newViewDate = this.moveMonth(this.viewDate, dir);
17134                 } else {
17135                     newDate = new Date(this.date);
17136                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17137                     newViewDate = new Date(this.viewDate);
17138                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17139                 }
17140                 if (this.dateWithinRange(newDate)){
17141                     this.date = newDate;
17142                     this.viewDate = newViewDate;
17143                     this.setValue(this.formatDate(this.date));
17144 //                    this.update();
17145                     e.preventDefault();
17146                     dateChanged = true;
17147                 }
17148                 break;
17149             case 13: // enter
17150                 this.setValue(this.formatDate(this.date));
17151                 this.hide();
17152                 e.preventDefault();
17153                 break;
17154             case 9: // tab
17155                 this.setValue(this.formatDate(this.date));
17156                 this.hide();
17157                 break;
17158             case 16: // shift
17159             case 17: // ctrl
17160             case 18: // alt
17161                 break;
17162             default :
17163                 this.hide();
17164                 
17165         }
17166     },
17167     
17168     
17169     onClick: function(e) 
17170     {
17171         e.stopPropagation();
17172         e.preventDefault();
17173         
17174         var target = e.getTarget();
17175         
17176         if(target.nodeName.toLowerCase() === 'i'){
17177             target = Roo.get(target).dom.parentNode;
17178         }
17179         
17180         var nodeName = target.nodeName;
17181         var className = target.className;
17182         var html = target.innerHTML;
17183         //Roo.log(nodeName);
17184         
17185         switch(nodeName.toLowerCase()) {
17186             case 'th':
17187                 switch(className) {
17188                     case 'switch':
17189                         this.showMode(1);
17190                         break;
17191                     case 'prev':
17192                     case 'next':
17193                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17194                         switch(this.viewMode){
17195                                 case 0:
17196                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17197                                         break;
17198                                 case 1:
17199                                 case 2:
17200                                         this.viewDate = this.moveYear(this.viewDate, dir);
17201                                         break;
17202                         }
17203                         this.fill();
17204                         break;
17205                     case 'today':
17206                         var date = new Date();
17207                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17208 //                        this.fill()
17209                         this.setValue(this.formatDate(this.date));
17210                         
17211                         this.hide();
17212                         break;
17213                 }
17214                 break;
17215             case 'span':
17216                 if (className.indexOf('disabled') < 0) {
17217                     this.viewDate.setUTCDate(1);
17218                     if (className.indexOf('month') > -1) {
17219                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17220                     } else {
17221                         var year = parseInt(html, 10) || 0;
17222                         this.viewDate.setUTCFullYear(year);
17223                         
17224                     }
17225                     
17226                     if(this.singleMode){
17227                         this.setValue(this.formatDate(this.viewDate));
17228                         this.hide();
17229                         return;
17230                     }
17231                     
17232                     this.showMode(-1);
17233                     this.fill();
17234                 }
17235                 break;
17236                 
17237             case 'td':
17238                 //Roo.log(className);
17239                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17240                     var day = parseInt(html, 10) || 1;
17241                     var year = this.viewDate.getUTCFullYear(),
17242                         month = this.viewDate.getUTCMonth();
17243
17244                     if (className.indexOf('old') > -1) {
17245                         if(month === 0 ){
17246                             month = 11;
17247                             year -= 1;
17248                         }else{
17249                             month -= 1;
17250                         }
17251                     } else if (className.indexOf('new') > -1) {
17252                         if (month == 11) {
17253                             month = 0;
17254                             year += 1;
17255                         } else {
17256                             month += 1;
17257                         }
17258                     }
17259                     //Roo.log([year,month,day]);
17260                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17261                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17262 //                    this.fill();
17263                     //Roo.log(this.formatDate(this.date));
17264                     this.setValue(this.formatDate(this.date));
17265                     this.hide();
17266                 }
17267                 break;
17268         }
17269     },
17270     
17271     setStartDate: function(startDate)
17272     {
17273         this.startDate = startDate || -Infinity;
17274         if (this.startDate !== -Infinity) {
17275             this.startDate = this.parseDate(this.startDate);
17276         }
17277         this.update();
17278         this.updateNavArrows();
17279     },
17280
17281     setEndDate: function(endDate)
17282     {
17283         this.endDate = endDate || Infinity;
17284         if (this.endDate !== Infinity) {
17285             this.endDate = this.parseDate(this.endDate);
17286         }
17287         this.update();
17288         this.updateNavArrows();
17289     },
17290     
17291     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17292     {
17293         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17294         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17295             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17296         }
17297         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17298             return parseInt(d, 10);
17299         });
17300         this.update();
17301         this.updateNavArrows();
17302     },
17303     
17304     updateNavArrows: function() 
17305     {
17306         if(this.singleMode){
17307             return;
17308         }
17309         
17310         var d = new Date(this.viewDate),
17311         year = d.getUTCFullYear(),
17312         month = d.getUTCMonth();
17313         
17314         Roo.each(this.picker().select('.prev', true).elements, function(v){
17315             v.show();
17316             switch (this.viewMode) {
17317                 case 0:
17318
17319                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17320                         v.hide();
17321                     }
17322                     break;
17323                 case 1:
17324                 case 2:
17325                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17326                         v.hide();
17327                     }
17328                     break;
17329             }
17330         });
17331         
17332         Roo.each(this.picker().select('.next', true).elements, function(v){
17333             v.show();
17334             switch (this.viewMode) {
17335                 case 0:
17336
17337                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17338                         v.hide();
17339                     }
17340                     break;
17341                 case 1:
17342                 case 2:
17343                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17344                         v.hide();
17345                     }
17346                     break;
17347             }
17348         })
17349     },
17350     
17351     moveMonth: function(date, dir)
17352     {
17353         if (!dir) {
17354             return date;
17355         }
17356         var new_date = new Date(date.valueOf()),
17357         day = new_date.getUTCDate(),
17358         month = new_date.getUTCMonth(),
17359         mag = Math.abs(dir),
17360         new_month, test;
17361         dir = dir > 0 ? 1 : -1;
17362         if (mag == 1){
17363             test = dir == -1
17364             // If going back one month, make sure month is not current month
17365             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17366             ? function(){
17367                 return new_date.getUTCMonth() == month;
17368             }
17369             // If going forward one month, make sure month is as expected
17370             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17371             : function(){
17372                 return new_date.getUTCMonth() != new_month;
17373             };
17374             new_month = month + dir;
17375             new_date.setUTCMonth(new_month);
17376             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17377             if (new_month < 0 || new_month > 11) {
17378                 new_month = (new_month + 12) % 12;
17379             }
17380         } else {
17381             // For magnitudes >1, move one month at a time...
17382             for (var i=0; i<mag; i++) {
17383                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17384                 new_date = this.moveMonth(new_date, dir);
17385             }
17386             // ...then reset the day, keeping it in the new month
17387             new_month = new_date.getUTCMonth();
17388             new_date.setUTCDate(day);
17389             test = function(){
17390                 return new_month != new_date.getUTCMonth();
17391             };
17392         }
17393         // Common date-resetting loop -- if date is beyond end of month, make it
17394         // end of month
17395         while (test()){
17396             new_date.setUTCDate(--day);
17397             new_date.setUTCMonth(new_month);
17398         }
17399         return new_date;
17400     },
17401
17402     moveYear: function(date, dir)
17403     {
17404         return this.moveMonth(date, dir*12);
17405     },
17406
17407     dateWithinRange: function(date)
17408     {
17409         return date >= this.startDate && date <= this.endDate;
17410     },
17411
17412     
17413     remove: function() 
17414     {
17415         this.picker().remove();
17416     }
17417    
17418 });
17419
17420 Roo.apply(Roo.bootstrap.DateField,  {
17421     
17422     head : {
17423         tag: 'thead',
17424         cn: [
17425         {
17426             tag: 'tr',
17427             cn: [
17428             {
17429                 tag: 'th',
17430                 cls: 'prev',
17431                 html: '<i class="fa fa-arrow-left"/>'
17432             },
17433             {
17434                 tag: 'th',
17435                 cls: 'switch',
17436                 colspan: '5'
17437             },
17438             {
17439                 tag: 'th',
17440                 cls: 'next',
17441                 html: '<i class="fa fa-arrow-right"/>'
17442             }
17443
17444             ]
17445         }
17446         ]
17447     },
17448     
17449     content : {
17450         tag: 'tbody',
17451         cn: [
17452         {
17453             tag: 'tr',
17454             cn: [
17455             {
17456                 tag: 'td',
17457                 colspan: '7'
17458             }
17459             ]
17460         }
17461         ]
17462     },
17463     
17464     footer : {
17465         tag: 'tfoot',
17466         cn: [
17467         {
17468             tag: 'tr',
17469             cn: [
17470             {
17471                 tag: 'th',
17472                 colspan: '7',
17473                 cls: 'today'
17474             }
17475                     
17476             ]
17477         }
17478         ]
17479     },
17480     
17481     dates:{
17482         en: {
17483             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17484             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17485             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17486             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17487             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17488             today: "Today"
17489         }
17490     },
17491     
17492     modes: [
17493     {
17494         clsName: 'days',
17495         navFnc: 'Month',
17496         navStep: 1
17497     },
17498     {
17499         clsName: 'months',
17500         navFnc: 'FullYear',
17501         navStep: 1
17502     },
17503     {
17504         clsName: 'years',
17505         navFnc: 'FullYear',
17506         navStep: 10
17507     }]
17508 });
17509
17510 Roo.apply(Roo.bootstrap.DateField,  {
17511   
17512     template : {
17513         tag: 'div',
17514         cls: 'datepicker dropdown-menu roo-dynamic',
17515         cn: [
17516         {
17517             tag: 'div',
17518             cls: 'datepicker-days',
17519             cn: [
17520             {
17521                 tag: 'table',
17522                 cls: 'table-condensed',
17523                 cn:[
17524                 Roo.bootstrap.DateField.head,
17525                 {
17526                     tag: 'tbody'
17527                 },
17528                 Roo.bootstrap.DateField.footer
17529                 ]
17530             }
17531             ]
17532         },
17533         {
17534             tag: 'div',
17535             cls: 'datepicker-months',
17536             cn: [
17537             {
17538                 tag: 'table',
17539                 cls: 'table-condensed',
17540                 cn:[
17541                 Roo.bootstrap.DateField.head,
17542                 Roo.bootstrap.DateField.content,
17543                 Roo.bootstrap.DateField.footer
17544                 ]
17545             }
17546             ]
17547         },
17548         {
17549             tag: 'div',
17550             cls: 'datepicker-years',
17551             cn: [
17552             {
17553                 tag: 'table',
17554                 cls: 'table-condensed',
17555                 cn:[
17556                 Roo.bootstrap.DateField.head,
17557                 Roo.bootstrap.DateField.content,
17558                 Roo.bootstrap.DateField.footer
17559                 ]
17560             }
17561             ]
17562         }
17563         ]
17564     }
17565 });
17566
17567  
17568
17569  /*
17570  * - LGPL
17571  *
17572  * TimeField
17573  * 
17574  */
17575
17576 /**
17577  * @class Roo.bootstrap.TimeField
17578  * @extends Roo.bootstrap.Input
17579  * Bootstrap DateField class
17580  * 
17581  * 
17582  * @constructor
17583  * Create a new TimeField
17584  * @param {Object} config The config object
17585  */
17586
17587 Roo.bootstrap.TimeField = function(config){
17588     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17589     this.addEvents({
17590             /**
17591              * @event show
17592              * Fires when this field show.
17593              * @param {Roo.bootstrap.DateField} thisthis
17594              * @param {Mixed} date The date value
17595              */
17596             show : true,
17597             /**
17598              * @event show
17599              * Fires when this field hide.
17600              * @param {Roo.bootstrap.DateField} this
17601              * @param {Mixed} date The date value
17602              */
17603             hide : true,
17604             /**
17605              * @event select
17606              * Fires when select a date.
17607              * @param {Roo.bootstrap.DateField} this
17608              * @param {Mixed} date The date value
17609              */
17610             select : true
17611         });
17612 };
17613
17614 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17615     
17616     /**
17617      * @cfg {String} format
17618      * The default time format string which can be overriden for localization support.  The format must be
17619      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17620      */
17621     format : "H:i",
17622        
17623     onRender: function(ct, position)
17624     {
17625         
17626         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17627                 
17628         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17629         
17630         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17631         
17632         this.pop = this.picker().select('>.datepicker-time',true).first();
17633         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17634         
17635         this.picker().on('mousedown', this.onMousedown, this);
17636         this.picker().on('click', this.onClick, this);
17637         
17638         this.picker().addClass('datepicker-dropdown');
17639     
17640         this.fillTime();
17641         this.update();
17642             
17643         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17644         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17645         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17646         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17647         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17648         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17649
17650     },
17651     
17652     fireKey: function(e){
17653         if (!this.picker().isVisible()){
17654             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17655                 this.show();
17656             }
17657             return;
17658         }
17659
17660         e.preventDefault();
17661         
17662         switch(e.keyCode){
17663             case 27: // escape
17664                 this.hide();
17665                 break;
17666             case 37: // left
17667             case 39: // right
17668                 this.onTogglePeriod();
17669                 break;
17670             case 38: // up
17671                 this.onIncrementMinutes();
17672                 break;
17673             case 40: // down
17674                 this.onDecrementMinutes();
17675                 break;
17676             case 13: // enter
17677             case 9: // tab
17678                 this.setTime();
17679                 break;
17680         }
17681     },
17682     
17683     onClick: function(e) {
17684         e.stopPropagation();
17685         e.preventDefault();
17686     },
17687     
17688     picker : function()
17689     {
17690         return this.el.select('.datepicker', true).first();
17691     },
17692     
17693     fillTime: function()
17694     {    
17695         var time = this.pop.select('tbody', true).first();
17696         
17697         time.dom.innerHTML = '';
17698         
17699         time.createChild({
17700             tag: 'tr',
17701             cn: [
17702                 {
17703                     tag: 'td',
17704                     cn: [
17705                         {
17706                             tag: 'a',
17707                             href: '#',
17708                             cls: 'btn',
17709                             cn: [
17710                                 {
17711                                     tag: 'span',
17712                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17713                                 }
17714                             ]
17715                         } 
17716                     ]
17717                 },
17718                 {
17719                     tag: 'td',
17720                     cls: 'separator'
17721                 },
17722                 {
17723                     tag: 'td',
17724                     cn: [
17725                         {
17726                             tag: 'a',
17727                             href: '#',
17728                             cls: 'btn',
17729                             cn: [
17730                                 {
17731                                     tag: 'span',
17732                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17733                                 }
17734                             ]
17735                         }
17736                     ]
17737                 },
17738                 {
17739                     tag: 'td',
17740                     cls: 'separator'
17741                 }
17742             ]
17743         });
17744         
17745         time.createChild({
17746             tag: 'tr',
17747             cn: [
17748                 {
17749                     tag: 'td',
17750                     cn: [
17751                         {
17752                             tag: 'span',
17753                             cls: 'timepicker-hour',
17754                             html: '00'
17755                         }  
17756                     ]
17757                 },
17758                 {
17759                     tag: 'td',
17760                     cls: 'separator',
17761                     html: ':'
17762                 },
17763                 {
17764                     tag: 'td',
17765                     cn: [
17766                         {
17767                             tag: 'span',
17768                             cls: 'timepicker-minute',
17769                             html: '00'
17770                         }  
17771                     ]
17772                 },
17773                 {
17774                     tag: 'td',
17775                     cls: 'separator'
17776                 },
17777                 {
17778                     tag: 'td',
17779                     cn: [
17780                         {
17781                             tag: 'button',
17782                             type: 'button',
17783                             cls: 'btn btn-primary period',
17784                             html: 'AM'
17785                             
17786                         }
17787                     ]
17788                 }
17789             ]
17790         });
17791         
17792         time.createChild({
17793             tag: 'tr',
17794             cn: [
17795                 {
17796                     tag: 'td',
17797                     cn: [
17798                         {
17799                             tag: 'a',
17800                             href: '#',
17801                             cls: 'btn',
17802                             cn: [
17803                                 {
17804                                     tag: 'span',
17805                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17806                                 }
17807                             ]
17808                         }
17809                     ]
17810                 },
17811                 {
17812                     tag: 'td',
17813                     cls: 'separator'
17814                 },
17815                 {
17816                     tag: 'td',
17817                     cn: [
17818                         {
17819                             tag: 'a',
17820                             href: '#',
17821                             cls: 'btn',
17822                             cn: [
17823                                 {
17824                                     tag: 'span',
17825                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17826                                 }
17827                             ]
17828                         }
17829                     ]
17830                 },
17831                 {
17832                     tag: 'td',
17833                     cls: 'separator'
17834                 }
17835             ]
17836         });
17837         
17838     },
17839     
17840     update: function()
17841     {
17842         
17843         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17844         
17845         this.fill();
17846     },
17847     
17848     fill: function() 
17849     {
17850         var hours = this.time.getHours();
17851         var minutes = this.time.getMinutes();
17852         var period = 'AM';
17853         
17854         if(hours > 11){
17855             period = 'PM';
17856         }
17857         
17858         if(hours == 0){
17859             hours = 12;
17860         }
17861         
17862         
17863         if(hours > 12){
17864             hours = hours - 12;
17865         }
17866         
17867         if(hours < 10){
17868             hours = '0' + hours;
17869         }
17870         
17871         if(minutes < 10){
17872             minutes = '0' + minutes;
17873         }
17874         
17875         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17876         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17877         this.pop.select('button', true).first().dom.innerHTML = period;
17878         
17879     },
17880     
17881     place: function()
17882     {   
17883         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17884         
17885         var cls = ['bottom'];
17886         
17887         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17888             cls.pop();
17889             cls.push('top');
17890         }
17891         
17892         cls.push('right');
17893         
17894         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17895             cls.pop();
17896             cls.push('left');
17897         }
17898         
17899         this.picker().addClass(cls.join('-'));
17900         
17901         var _this = this;
17902         
17903         Roo.each(cls, function(c){
17904             if(c == 'bottom'){
17905                 _this.picker().setTop(_this.inputEl().getHeight());
17906                 return;
17907             }
17908             if(c == 'top'){
17909                 _this.picker().setTop(0 - _this.picker().getHeight());
17910                 return;
17911             }
17912             
17913             if(c == 'left'){
17914                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17915                 return;
17916             }
17917             if(c == 'right'){
17918                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17919                 return;
17920             }
17921         });
17922         
17923     },
17924   
17925     onFocus : function()
17926     {
17927         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17928         this.show();
17929     },
17930     
17931     onBlur : function()
17932     {
17933         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17934         this.hide();
17935     },
17936     
17937     show : function()
17938     {
17939         this.picker().show();
17940         this.pop.show();
17941         this.update();
17942         this.place();
17943         
17944         this.fireEvent('show', this, this.date);
17945     },
17946     
17947     hide : function()
17948     {
17949         this.picker().hide();
17950         this.pop.hide();
17951         
17952         this.fireEvent('hide', this, this.date);
17953     },
17954     
17955     setTime : function()
17956     {
17957         this.hide();
17958         this.setValue(this.time.format(this.format));
17959         
17960         this.fireEvent('select', this, this.date);
17961         
17962         
17963     },
17964     
17965     onMousedown: function(e){
17966         e.stopPropagation();
17967         e.preventDefault();
17968     },
17969     
17970     onIncrementHours: function()
17971     {
17972         Roo.log('onIncrementHours');
17973         this.time = this.time.add(Date.HOUR, 1);
17974         this.update();
17975         
17976     },
17977     
17978     onDecrementHours: function()
17979     {
17980         Roo.log('onDecrementHours');
17981         this.time = this.time.add(Date.HOUR, -1);
17982         this.update();
17983     },
17984     
17985     onIncrementMinutes: function()
17986     {
17987         Roo.log('onIncrementMinutes');
17988         this.time = this.time.add(Date.MINUTE, 1);
17989         this.update();
17990     },
17991     
17992     onDecrementMinutes: function()
17993     {
17994         Roo.log('onDecrementMinutes');
17995         this.time = this.time.add(Date.MINUTE, -1);
17996         this.update();
17997     },
17998     
17999     onTogglePeriod: function()
18000     {
18001         Roo.log('onTogglePeriod');
18002         this.time = this.time.add(Date.HOUR, 12);
18003         this.update();
18004     }
18005     
18006    
18007 });
18008
18009 Roo.apply(Roo.bootstrap.TimeField,  {
18010     
18011     content : {
18012         tag: 'tbody',
18013         cn: [
18014             {
18015                 tag: 'tr',
18016                 cn: [
18017                 {
18018                     tag: 'td',
18019                     colspan: '7'
18020                 }
18021                 ]
18022             }
18023         ]
18024     },
18025     
18026     footer : {
18027         tag: 'tfoot',
18028         cn: [
18029             {
18030                 tag: 'tr',
18031                 cn: [
18032                 {
18033                     tag: 'th',
18034                     colspan: '7',
18035                     cls: '',
18036                     cn: [
18037                         {
18038                             tag: 'button',
18039                             cls: 'btn btn-info ok',
18040                             html: 'OK'
18041                         }
18042                     ]
18043                 }
18044
18045                 ]
18046             }
18047         ]
18048     }
18049 });
18050
18051 Roo.apply(Roo.bootstrap.TimeField,  {
18052   
18053     template : {
18054         tag: 'div',
18055         cls: 'datepicker dropdown-menu',
18056         cn: [
18057             {
18058                 tag: 'div',
18059                 cls: 'datepicker-time',
18060                 cn: [
18061                 {
18062                     tag: 'table',
18063                     cls: 'table-condensed',
18064                     cn:[
18065                     Roo.bootstrap.TimeField.content,
18066                     Roo.bootstrap.TimeField.footer
18067                     ]
18068                 }
18069                 ]
18070             }
18071         ]
18072     }
18073 });
18074
18075  
18076
18077  /*
18078  * - LGPL
18079  *
18080  * MonthField
18081  * 
18082  */
18083
18084 /**
18085  * @class Roo.bootstrap.MonthField
18086  * @extends Roo.bootstrap.Input
18087  * Bootstrap MonthField class
18088  * 
18089  * @cfg {String} language default en
18090  * 
18091  * @constructor
18092  * Create a new MonthField
18093  * @param {Object} config The config object
18094  */
18095
18096 Roo.bootstrap.MonthField = function(config){
18097     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18098     
18099     this.addEvents({
18100         /**
18101          * @event show
18102          * Fires when this field show.
18103          * @param {Roo.bootstrap.MonthField} this
18104          * @param {Mixed} date The date value
18105          */
18106         show : true,
18107         /**
18108          * @event show
18109          * Fires when this field hide.
18110          * @param {Roo.bootstrap.MonthField} this
18111          * @param {Mixed} date The date value
18112          */
18113         hide : true,
18114         /**
18115          * @event select
18116          * Fires when select a date.
18117          * @param {Roo.bootstrap.MonthField} this
18118          * @param {String} oldvalue The old value
18119          * @param {String} newvalue The new value
18120          */
18121         select : true
18122     });
18123 };
18124
18125 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18126     
18127     onRender: function(ct, position)
18128     {
18129         
18130         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18131         
18132         this.language = this.language || 'en';
18133         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18134         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18135         
18136         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18137         this.isInline = false;
18138         this.isInput = true;
18139         this.component = this.el.select('.add-on', true).first() || false;
18140         this.component = (this.component && this.component.length === 0) ? false : this.component;
18141         this.hasInput = this.component && this.inputEL().length;
18142         
18143         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18144         
18145         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18146         
18147         this.picker().on('mousedown', this.onMousedown, this);
18148         this.picker().on('click', this.onClick, this);
18149         
18150         this.picker().addClass('datepicker-dropdown');
18151         
18152         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18153             v.setStyle('width', '189px');
18154         });
18155         
18156         this.fillMonths();
18157         
18158         this.update();
18159         
18160         if(this.isInline) {
18161             this.show();
18162         }
18163         
18164     },
18165     
18166     setValue: function(v, suppressEvent)
18167     {   
18168         var o = this.getValue();
18169         
18170         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18171         
18172         this.update();
18173
18174         if(suppressEvent !== true){
18175             this.fireEvent('select', this, o, v);
18176         }
18177         
18178     },
18179     
18180     getValue: function()
18181     {
18182         return this.value;
18183     },
18184     
18185     onClick: function(e) 
18186     {
18187         e.stopPropagation();
18188         e.preventDefault();
18189         
18190         var target = e.getTarget();
18191         
18192         if(target.nodeName.toLowerCase() === 'i'){
18193             target = Roo.get(target).dom.parentNode;
18194         }
18195         
18196         var nodeName = target.nodeName;
18197         var className = target.className;
18198         var html = target.innerHTML;
18199         
18200         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18201             return;
18202         }
18203         
18204         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18205         
18206         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18207         
18208         this.hide();
18209                         
18210     },
18211     
18212     picker : function()
18213     {
18214         return this.pickerEl;
18215     },
18216     
18217     fillMonths: function()
18218     {    
18219         var i = 0;
18220         var months = this.picker().select('>.datepicker-months td', true).first();
18221         
18222         months.dom.innerHTML = '';
18223         
18224         while (i < 12) {
18225             var month = {
18226                 tag: 'span',
18227                 cls: 'month',
18228                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18229             };
18230             
18231             months.createChild(month);
18232         }
18233         
18234     },
18235     
18236     update: function()
18237     {
18238         var _this = this;
18239         
18240         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18241             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18242         }
18243         
18244         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18245             e.removeClass('active');
18246             
18247             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18248                 e.addClass('active');
18249             }
18250         })
18251     },
18252     
18253     place: function()
18254     {
18255         if(this.isInline) {
18256             return;
18257         }
18258         
18259         this.picker().removeClass(['bottom', 'top']);
18260         
18261         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18262             /*
18263              * place to the top of element!
18264              *
18265              */
18266             
18267             this.picker().addClass('top');
18268             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18269             
18270             return;
18271         }
18272         
18273         this.picker().addClass('bottom');
18274         
18275         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18276     },
18277     
18278     onFocus : function()
18279     {
18280         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18281         this.show();
18282     },
18283     
18284     onBlur : function()
18285     {
18286         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18287         
18288         var d = this.inputEl().getValue();
18289         
18290         this.setValue(d);
18291                 
18292         this.hide();
18293     },
18294     
18295     show : function()
18296     {
18297         this.picker().show();
18298         this.picker().select('>.datepicker-months', true).first().show();
18299         this.update();
18300         this.place();
18301         
18302         this.fireEvent('show', this, this.date);
18303     },
18304     
18305     hide : function()
18306     {
18307         if(this.isInline) {
18308             return;
18309         }
18310         this.picker().hide();
18311         this.fireEvent('hide', this, this.date);
18312         
18313     },
18314     
18315     onMousedown: function(e)
18316     {
18317         e.stopPropagation();
18318         e.preventDefault();
18319     },
18320     
18321     keyup: function(e)
18322     {
18323         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18324         this.update();
18325     },
18326
18327     fireKey: function(e)
18328     {
18329         if (!this.picker().isVisible()){
18330             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18331                 this.show();
18332             }
18333             return;
18334         }
18335         
18336         var dir;
18337         
18338         switch(e.keyCode){
18339             case 27: // escape
18340                 this.hide();
18341                 e.preventDefault();
18342                 break;
18343             case 37: // left
18344             case 39: // right
18345                 dir = e.keyCode == 37 ? -1 : 1;
18346                 
18347                 this.vIndex = this.vIndex + dir;
18348                 
18349                 if(this.vIndex < 0){
18350                     this.vIndex = 0;
18351                 }
18352                 
18353                 if(this.vIndex > 11){
18354                     this.vIndex = 11;
18355                 }
18356                 
18357                 if(isNaN(this.vIndex)){
18358                     this.vIndex = 0;
18359                 }
18360                 
18361                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18362                 
18363                 break;
18364             case 38: // up
18365             case 40: // down
18366                 
18367                 dir = e.keyCode == 38 ? -1 : 1;
18368                 
18369                 this.vIndex = this.vIndex + dir * 4;
18370                 
18371                 if(this.vIndex < 0){
18372                     this.vIndex = 0;
18373                 }
18374                 
18375                 if(this.vIndex > 11){
18376                     this.vIndex = 11;
18377                 }
18378                 
18379                 if(isNaN(this.vIndex)){
18380                     this.vIndex = 0;
18381                 }
18382                 
18383                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18384                 break;
18385                 
18386             case 13: // enter
18387                 
18388                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18389                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18390                 }
18391                 
18392                 this.hide();
18393                 e.preventDefault();
18394                 break;
18395             case 9: // tab
18396                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18397                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18398                 }
18399                 this.hide();
18400                 break;
18401             case 16: // shift
18402             case 17: // ctrl
18403             case 18: // alt
18404                 break;
18405             default :
18406                 this.hide();
18407                 
18408         }
18409     },
18410     
18411     remove: function() 
18412     {
18413         this.picker().remove();
18414     }
18415    
18416 });
18417
18418 Roo.apply(Roo.bootstrap.MonthField,  {
18419     
18420     content : {
18421         tag: 'tbody',
18422         cn: [
18423         {
18424             tag: 'tr',
18425             cn: [
18426             {
18427                 tag: 'td',
18428                 colspan: '7'
18429             }
18430             ]
18431         }
18432         ]
18433     },
18434     
18435     dates:{
18436         en: {
18437             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18438             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18439         }
18440     }
18441 });
18442
18443 Roo.apply(Roo.bootstrap.MonthField,  {
18444   
18445     template : {
18446         tag: 'div',
18447         cls: 'datepicker dropdown-menu roo-dynamic',
18448         cn: [
18449             {
18450                 tag: 'div',
18451                 cls: 'datepicker-months',
18452                 cn: [
18453                 {
18454                     tag: 'table',
18455                     cls: 'table-condensed',
18456                     cn:[
18457                         Roo.bootstrap.DateField.content
18458                     ]
18459                 }
18460                 ]
18461             }
18462         ]
18463     }
18464 });
18465
18466  
18467
18468  
18469  /*
18470  * - LGPL
18471  *
18472  * CheckBox
18473  * 
18474  */
18475
18476 /**
18477  * @class Roo.bootstrap.CheckBox
18478  * @extends Roo.bootstrap.Input
18479  * Bootstrap CheckBox class
18480  * 
18481  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18482  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18483  * @cfg {String} boxLabel The text that appears beside the checkbox
18484  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18485  * @cfg {Boolean} checked initnal the element
18486  * @cfg {Boolean} inline inline the element (default false)
18487  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18488  * 
18489  * @constructor
18490  * Create a new CheckBox
18491  * @param {Object} config The config object
18492  */
18493
18494 Roo.bootstrap.CheckBox = function(config){
18495     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18496    
18497     this.addEvents({
18498         /**
18499         * @event check
18500         * Fires when the element is checked or unchecked.
18501         * @param {Roo.bootstrap.CheckBox} this This input
18502         * @param {Boolean} checked The new checked value
18503         */
18504        check : true
18505     });
18506     
18507 };
18508
18509 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18510   
18511     inputType: 'checkbox',
18512     inputValue: 1,
18513     valueOff: 0,
18514     boxLabel: false,
18515     checked: false,
18516     weight : false,
18517     inline: false,
18518     
18519     getAutoCreate : function()
18520     {
18521         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18522         
18523         var id = Roo.id();
18524         
18525         var cfg = {};
18526         
18527         cfg.cls = 'form-group ' + this.inputType; //input-group
18528         
18529         if(this.inline){
18530             cfg.cls += ' ' + this.inputType + '-inline';
18531         }
18532         
18533         var input =  {
18534             tag: 'input',
18535             id : id,
18536             type : this.inputType,
18537             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18538             cls : 'roo-' + this.inputType, //'form-box',
18539             placeholder : this.placeholder || ''
18540             
18541         };
18542         
18543         if (this.weight) { // Validity check?
18544             cfg.cls += " " + this.inputType + "-" + this.weight;
18545         }
18546         
18547         if (this.disabled) {
18548             input.disabled=true;
18549         }
18550         
18551         if(this.checked){
18552             input.checked = this.checked;
18553         }
18554         
18555         if (this.name) {
18556             input.name = this.name;
18557         }
18558         
18559         if (this.size) {
18560             input.cls += ' input-' + this.size;
18561         }
18562         
18563         var settings=this;
18564         
18565         ['xs','sm','md','lg'].map(function(size){
18566             if (settings[size]) {
18567                 cfg.cls += ' col-' + size + '-' + settings[size];
18568             }
18569         });
18570         
18571         var inputblock = input;
18572          
18573         if (this.before || this.after) {
18574             
18575             inputblock = {
18576                 cls : 'input-group',
18577                 cn :  [] 
18578             };
18579             
18580             if (this.before) {
18581                 inputblock.cn.push({
18582                     tag :'span',
18583                     cls : 'input-group-addon',
18584                     html : this.before
18585                 });
18586             }
18587             
18588             inputblock.cn.push(input);
18589             
18590             if (this.after) {
18591                 inputblock.cn.push({
18592                     tag :'span',
18593                     cls : 'input-group-addon',
18594                     html : this.after
18595                 });
18596             }
18597             
18598         }
18599         
18600         if (align ==='left' && this.fieldLabel.length) {
18601                 Roo.log("left and has label");
18602                 cfg.cn = [
18603                     
18604                     {
18605                         tag: 'label',
18606                         'for' :  id,
18607                         cls : 'control-label col-md-' + this.labelWidth,
18608                         html : this.fieldLabel
18609                         
18610                     },
18611                     {
18612                         cls : "col-md-" + (12 - this.labelWidth), 
18613                         cn: [
18614                             inputblock
18615                         ]
18616                     }
18617                     
18618                 ];
18619         } else if ( this.fieldLabel.length) {
18620                 Roo.log(" label");
18621                 cfg.cn = [
18622                    
18623                     {
18624                         tag: this.boxLabel ? 'span' : 'label',
18625                         'for': id,
18626                         cls: 'control-label box-input-label',
18627                         //cls : 'input-group-addon',
18628                         html : this.fieldLabel
18629                         
18630                     },
18631                     
18632                     inputblock
18633                     
18634                 ];
18635
18636         } else {
18637             
18638                 Roo.log(" no label && no align");
18639                 cfg.cn = [  inputblock ] ;
18640                 
18641                 
18642         }
18643         if(this.boxLabel){
18644              var boxLabelCfg = {
18645                 tag: 'label',
18646                 //'for': id, // box label is handled by onclick - so no for...
18647                 cls: 'box-label',
18648                 html: this.boxLabel
18649             };
18650             
18651             if(this.tooltip){
18652                 boxLabelCfg.tooltip = this.tooltip;
18653             }
18654              
18655             cfg.cn.push(boxLabelCfg);
18656         }
18657         
18658         
18659        
18660         return cfg;
18661         
18662     },
18663     
18664     /**
18665      * return the real input element.
18666      */
18667     inputEl: function ()
18668     {
18669         return this.el.select('input.roo-' + this.inputType,true).first();
18670     },
18671     
18672     labelEl: function()
18673     {
18674         return this.el.select('label.control-label',true).first();
18675     },
18676     /* depricated... */
18677     
18678     label: function()
18679     {
18680         return this.labelEl();
18681     },
18682     
18683     initEvents : function()
18684     {
18685 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18686         
18687         this.inputEl().on('click', this.onClick,  this);
18688         
18689         if (this.boxLabel) { 
18690             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18691         }
18692         
18693         this.startValue = this.getValue();
18694         
18695         if(this.groupId){
18696             Roo.bootstrap.CheckBox.register(this);
18697         }
18698     },
18699     
18700     onClick : function()
18701     {   
18702         this.setChecked(!this.checked);
18703     },
18704     
18705     setChecked : function(state,suppressEvent)
18706     {
18707         this.startValue = this.getValue();
18708         
18709         if(this.inputType == 'radio'){
18710             
18711             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18712                 e.dom.checked = false;
18713             });
18714             
18715             this.inputEl().dom.checked = true;
18716             
18717             this.inputEl().dom.value = this.inputValue;
18718             
18719             if(suppressEvent !== true){
18720                 this.fireEvent('check', this, true);
18721             }
18722             
18723             this.validate();
18724             
18725             return;
18726         }
18727         
18728         this.checked = state;
18729         
18730         this.inputEl().dom.checked = state;
18731         
18732         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18733         
18734         if(suppressEvent !== true){
18735             this.fireEvent('check', this, state);
18736         }
18737         
18738         this.validate();
18739     },
18740     
18741     getValue : function()
18742     {
18743         if(this.inputType == 'radio'){
18744             return this.getGroupValue();
18745         }
18746         
18747         return this.inputEl().getValue();
18748         
18749     },
18750     
18751     getGroupValue : function()
18752     {
18753         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18754             return '';
18755         }
18756         
18757         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18758     },
18759     
18760     setValue : function(v,suppressEvent)
18761     {
18762         if(this.inputType == 'radio'){
18763             this.setGroupValue(v, suppressEvent);
18764             return;
18765         }
18766         
18767         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18768         
18769         this.validate();
18770     },
18771     
18772     setGroupValue : function(v, suppressEvent)
18773     {
18774         this.startValue = this.getValue();
18775         
18776         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18777             e.dom.checked = false;
18778             
18779             if(e.dom.value == v){
18780                 e.dom.checked = true;
18781             }
18782         });
18783         
18784         if(suppressEvent !== true){
18785             this.fireEvent('check', this, true);
18786         }
18787
18788         this.validate();
18789         
18790         return;
18791     },
18792     
18793     validate : function()
18794     {
18795         if(
18796                 this.disabled || 
18797                 (this.inputType == 'radio' && this.validateRadio()) ||
18798                 (this.inputType == 'checkbox' && this.validateCheckbox())
18799         ){
18800             this.markValid();
18801             return true;
18802         }
18803         
18804         this.markInvalid();
18805         return false;
18806     },
18807     
18808     validateRadio : function()
18809     {
18810         var valid = false;
18811         
18812         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18813             if(!e.dom.checked){
18814                 return;
18815             }
18816             
18817             valid = true;
18818             
18819             return false;
18820         });
18821         
18822         return valid;
18823     },
18824     
18825     validateCheckbox : function()
18826     {
18827         if(!this.groupId){
18828             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18829         }
18830         
18831         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18832         
18833         if(!group){
18834             return false;
18835         }
18836         
18837         var r = false;
18838         
18839         for(var i in group){
18840             if(r){
18841                 break;
18842             }
18843             
18844             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18845         }
18846         
18847         return r;
18848     },
18849     
18850     /**
18851      * Mark this field as valid
18852      */
18853     markValid : function()
18854     {
18855         if(this.allowBlank){
18856             return;
18857         }
18858         
18859         var _this = this;
18860         
18861         this.fireEvent('valid', this);
18862         
18863         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18864         
18865         if(this.groupId){
18866             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18867         }
18868         
18869         if(label){
18870             label.markValid();
18871         }
18872         
18873         if(this.inputType == 'radio'){
18874             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18875                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18876                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18877             });
18878             
18879             return;
18880         }
18881         
18882         if(!this.groupId){
18883             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18884             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18885             return;
18886         }
18887         
18888         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18889             
18890         if(!group){
18891             return;
18892         }
18893         
18894         for(var i in group){
18895             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18896             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18897         }
18898     },
18899     
18900      /**
18901      * Mark this field as invalid
18902      * @param {String} msg The validation message
18903      */
18904     markInvalid : function(msg)
18905     {
18906         if(this.allowBlank){
18907             return;
18908         }
18909         
18910         var _this = this;
18911         
18912         this.fireEvent('invalid', this, msg);
18913         
18914         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18915         
18916         if(this.groupId){
18917             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18918         }
18919         
18920         if(label){
18921             label.markInvalid();
18922         }
18923             
18924         if(this.inputType == 'radio'){
18925             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18926                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18927                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18928             });
18929             
18930             return;
18931         }
18932         
18933         if(!this.groupId){
18934             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18935             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18936             return;
18937         }
18938         
18939         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18940         
18941         if(!group){
18942             return;
18943         }
18944         
18945         for(var i in group){
18946             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18947             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18948         }
18949         
18950     }
18951     
18952 });
18953
18954 Roo.apply(Roo.bootstrap.CheckBox, {
18955     
18956     groups: {},
18957     
18958      /**
18959     * register a CheckBox Group
18960     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18961     */
18962     register : function(checkbox)
18963     {
18964         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18965             this.groups[checkbox.groupId] = {};
18966         }
18967         
18968         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18969             return;
18970         }
18971         
18972         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18973         
18974     },
18975     /**
18976     * fetch a CheckBox Group based on the group ID
18977     * @param {string} the group ID
18978     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18979     */
18980     get: function(groupId) {
18981         if (typeof(this.groups[groupId]) == 'undefined') {
18982             return false;
18983         }
18984         
18985         return this.groups[groupId] ;
18986     }
18987     
18988     
18989 });
18990 /*
18991  * - LGPL
18992  *
18993  * Radio
18994  *
18995  *
18996  * not inline
18997  *<div class="radio">
18998   <label>
18999     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19000     Option one is this and that&mdash;be sure to include why it's great
19001   </label>
19002 </div>
19003  *
19004  *
19005  *inline
19006  *<span>
19007  *<label class="radio-inline">fieldLabel</label>
19008  *<label class="radio-inline">
19009   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19010 </label>
19011 <span>
19012  * 
19013  * 
19014  */
19015
19016 /**
19017  * @class Roo.bootstrap.Radio
19018  * @extends Roo.bootstrap.CheckBox
19019  * Bootstrap Radio class
19020
19021  * @constructor
19022  * Create a new Radio
19023  * @param {Object} config The config object
19024  */
19025
19026 Roo.bootstrap.Radio = function(config){
19027     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19028    
19029 };
19030
19031 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19032     
19033     inputType: 'radio',
19034     inputValue: '',
19035     valueOff: '',
19036     
19037     getAutoCreate : function()
19038     {
19039         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19040         align = align || 'left'; // default...
19041         
19042         
19043         
19044         var id = Roo.id();
19045         
19046         var cfg = {
19047                 tag : this.inline ? 'span' : 'div',
19048                 cls : '',
19049                 cn : []
19050         };
19051         
19052         var inline = this.inline ? ' radio-inline' : '';
19053         
19054         var lbl = {
19055                 tag: 'label' ,
19056                 // does not need for, as we wrap the input with it..
19057                 'for' : id,
19058                 cls : 'control-label box-label' + inline,
19059                 cn : []
19060         };
19061         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19062         
19063         var fieldLabel = {
19064             tag: 'label' ,
19065             //cls : 'control-label' + inline,
19066             html : this.fieldLabel,
19067             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19068         };
19069         
19070  
19071         
19072         
19073         var input =  {
19074             tag: 'input',
19075             id : id,
19076             type : this.inputType,
19077             //value : (!this.checked) ? this.valueOff : this.inputValue,
19078             value : this.inputValue,
19079             cls : 'roo-radio',
19080             placeholder : this.placeholder || '' // ?? needed????
19081             
19082         };
19083         if (this.weight) { // Validity check?
19084             input.cls += " radio-" + this.weight;
19085         }
19086         if (this.disabled) {
19087             input.disabled=true;
19088         }
19089         
19090         if(this.checked){
19091             input.checked = this.checked;
19092         }
19093         
19094         if (this.name) {
19095             input.name = this.name;
19096         }
19097         
19098         if (this.size) {
19099             input.cls += ' input-' + this.size;
19100         }
19101         
19102         //?? can span's inline have a width??
19103         
19104         var settings=this;
19105         ['xs','sm','md','lg'].map(function(size){
19106             if (settings[size]) {
19107                 cfg.cls += ' col-' + size + '-' + settings[size];
19108             }
19109         });
19110         
19111         var inputblock = input;
19112         
19113         if (this.before || this.after) {
19114             
19115             inputblock = {
19116                 cls : 'input-group',
19117                 tag : 'span',
19118                 cn :  [] 
19119             };
19120             if (this.before) {
19121                 inputblock.cn.push({
19122                     tag :'span',
19123                     cls : 'input-group-addon',
19124                     html : this.before
19125                 });
19126             }
19127             inputblock.cn.push(input);
19128             if (this.after) {
19129                 inputblock.cn.push({
19130                     tag :'span',
19131                     cls : 'input-group-addon',
19132                     html : this.after
19133                 });
19134             }
19135             
19136         };
19137         
19138         
19139         if (this.fieldLabel && this.fieldLabel.length) {
19140             cfg.cn.push(fieldLabel);
19141         }
19142        
19143         // normal bootstrap puts the input inside the label.
19144         // however with our styled version - it has to go after the input.
19145        
19146         //lbl.cn.push(inputblock);
19147         
19148         var lblwrap =  {
19149             tag: 'span',
19150             cls: 'radio' + inline,
19151             cn: [
19152                 inputblock,
19153                 lbl
19154             ]
19155         };
19156         
19157         cfg.cn.push( lblwrap);
19158         
19159         if(this.boxLabel){
19160             lbl.cn.push({
19161                 tag: 'span',
19162                 html: this.boxLabel
19163             })
19164         }
19165          
19166         
19167         return cfg;
19168         
19169     },
19170     
19171     initEvents : function()
19172     {
19173 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19174         
19175         this.inputEl().on('click', this.onClick,  this);
19176         if (this.boxLabel) {
19177             //Roo.log('find label');
19178             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19179         }
19180         
19181     },
19182     
19183     inputEl: function ()
19184     {
19185         return this.el.select('input.roo-radio',true).first();
19186     },
19187     onClick : function()
19188     {   
19189         Roo.log("click");
19190         this.setChecked(true);
19191     },
19192     
19193     setChecked : function(state,suppressEvent)
19194     {
19195         if(state){
19196             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19197                 v.dom.checked = false;
19198             });
19199         }
19200         Roo.log(this.inputEl().dom);
19201         this.checked = state;
19202         this.inputEl().dom.checked = state;
19203         
19204         if(suppressEvent !== true){
19205             this.fireEvent('check', this, state);
19206         }
19207         
19208         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19209         
19210     },
19211     
19212     getGroupValue : function()
19213     {
19214         var value = '';
19215         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19216             if(v.dom.checked == true){
19217                 value = v.dom.value;
19218             }
19219         });
19220         
19221         return value;
19222     },
19223     
19224     /**
19225      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19226      * @return {Mixed} value The field value
19227      */
19228     getValue : function(){
19229         return this.getGroupValue();
19230     }
19231     
19232 });
19233
19234  
19235 //<script type="text/javascript">
19236
19237 /*
19238  * Based  Ext JS Library 1.1.1
19239  * Copyright(c) 2006-2007, Ext JS, LLC.
19240  * LGPL
19241  *
19242  */
19243  
19244 /**
19245  * @class Roo.HtmlEditorCore
19246  * @extends Roo.Component
19247  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19248  *
19249  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19250  */
19251
19252 Roo.HtmlEditorCore = function(config){
19253     
19254     
19255     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19256     
19257     
19258     this.addEvents({
19259         /**
19260          * @event initialize
19261          * Fires when the editor is fully initialized (including the iframe)
19262          * @param {Roo.HtmlEditorCore} this
19263          */
19264         initialize: true,
19265         /**
19266          * @event activate
19267          * Fires when the editor is first receives the focus. Any insertion must wait
19268          * until after this event.
19269          * @param {Roo.HtmlEditorCore} this
19270          */
19271         activate: true,
19272          /**
19273          * @event beforesync
19274          * Fires before the textarea is updated with content from the editor iframe. Return false
19275          * to cancel the sync.
19276          * @param {Roo.HtmlEditorCore} this
19277          * @param {String} html
19278          */
19279         beforesync: true,
19280          /**
19281          * @event beforepush
19282          * Fires before the iframe editor is updated with content from the textarea. Return false
19283          * to cancel the push.
19284          * @param {Roo.HtmlEditorCore} this
19285          * @param {String} html
19286          */
19287         beforepush: true,
19288          /**
19289          * @event sync
19290          * Fires when the textarea is updated with content from the editor iframe.
19291          * @param {Roo.HtmlEditorCore} this
19292          * @param {String} html
19293          */
19294         sync: true,
19295          /**
19296          * @event push
19297          * Fires when the iframe editor is updated with content from the textarea.
19298          * @param {Roo.HtmlEditorCore} this
19299          * @param {String} html
19300          */
19301         push: true,
19302         
19303         /**
19304          * @event editorevent
19305          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19306          * @param {Roo.HtmlEditorCore} this
19307          */
19308         editorevent: true
19309         
19310     });
19311     
19312     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19313     
19314     // defaults : white / black...
19315     this.applyBlacklists();
19316     
19317     
19318     
19319 };
19320
19321
19322 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19323
19324
19325      /**
19326      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19327      */
19328     
19329     owner : false,
19330     
19331      /**
19332      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19333      *                        Roo.resizable.
19334      */
19335     resizable : false,
19336      /**
19337      * @cfg {Number} height (in pixels)
19338      */   
19339     height: 300,
19340    /**
19341      * @cfg {Number} width (in pixels)
19342      */   
19343     width: 500,
19344     
19345     /**
19346      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19347      * 
19348      */
19349     stylesheets: false,
19350     
19351     // id of frame..
19352     frameId: false,
19353     
19354     // private properties
19355     validationEvent : false,
19356     deferHeight: true,
19357     initialized : false,
19358     activated : false,
19359     sourceEditMode : false,
19360     onFocus : Roo.emptyFn,
19361     iframePad:3,
19362     hideMode:'offsets',
19363     
19364     clearUp: true,
19365     
19366     // blacklist + whitelisted elements..
19367     black: false,
19368     white: false,
19369      
19370     
19371
19372     /**
19373      * Protected method that will not generally be called directly. It
19374      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19375      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19376      */
19377     getDocMarkup : function(){
19378         // body styles..
19379         var st = '';
19380         
19381         // inherit styels from page...?? 
19382         if (this.stylesheets === false) {
19383             
19384             Roo.get(document.head).select('style').each(function(node) {
19385                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19386             });
19387             
19388             Roo.get(document.head).select('link').each(function(node) { 
19389                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19390             });
19391             
19392         } else if (!this.stylesheets.length) {
19393                 // simple..
19394                 st = '<style type="text/css">' +
19395                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19396                    '</style>';
19397         } else { 
19398             
19399         }
19400         
19401         st +=  '<style type="text/css">' +
19402             'IMG { cursor: pointer } ' +
19403         '</style>';
19404
19405         
19406         return '<html><head>' + st  +
19407             //<style type="text/css">' +
19408             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19409             //'</style>' +
19410             ' </head><body class="roo-htmleditor-body"></body></html>';
19411     },
19412
19413     // private
19414     onRender : function(ct, position)
19415     {
19416         var _t = this;
19417         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19418         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19419         
19420         
19421         this.el.dom.style.border = '0 none';
19422         this.el.dom.setAttribute('tabIndex', -1);
19423         this.el.addClass('x-hidden hide');
19424         
19425         
19426         
19427         if(Roo.isIE){ // fix IE 1px bogus margin
19428             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19429         }
19430        
19431         
19432         this.frameId = Roo.id();
19433         
19434          
19435         
19436         var iframe = this.owner.wrap.createChild({
19437             tag: 'iframe',
19438             cls: 'form-control', // bootstrap..
19439             id: this.frameId,
19440             name: this.frameId,
19441             frameBorder : 'no',
19442             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19443         }, this.el
19444         );
19445         
19446         
19447         this.iframe = iframe.dom;
19448
19449          this.assignDocWin();
19450         
19451         this.doc.designMode = 'on';
19452        
19453         this.doc.open();
19454         this.doc.write(this.getDocMarkup());
19455         this.doc.close();
19456
19457         
19458         var task = { // must defer to wait for browser to be ready
19459             run : function(){
19460                 //console.log("run task?" + this.doc.readyState);
19461                 this.assignDocWin();
19462                 if(this.doc.body || this.doc.readyState == 'complete'){
19463                     try {
19464                         this.doc.designMode="on";
19465                     } catch (e) {
19466                         return;
19467                     }
19468                     Roo.TaskMgr.stop(task);
19469                     this.initEditor.defer(10, this);
19470                 }
19471             },
19472             interval : 10,
19473             duration: 10000,
19474             scope: this
19475         };
19476         Roo.TaskMgr.start(task);
19477
19478     },
19479
19480     // private
19481     onResize : function(w, h)
19482     {
19483          Roo.log('resize: ' +w + ',' + h );
19484         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19485         if(!this.iframe){
19486             return;
19487         }
19488         if(typeof w == 'number'){
19489             
19490             this.iframe.style.width = w + 'px';
19491         }
19492         if(typeof h == 'number'){
19493             
19494             this.iframe.style.height = h + 'px';
19495             if(this.doc){
19496                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19497             }
19498         }
19499         
19500     },
19501
19502     /**
19503      * Toggles the editor between standard and source edit mode.
19504      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19505      */
19506     toggleSourceEdit : function(sourceEditMode){
19507         
19508         this.sourceEditMode = sourceEditMode === true;
19509         
19510         if(this.sourceEditMode){
19511  
19512             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19513             
19514         }else{
19515             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19516             //this.iframe.className = '';
19517             this.deferFocus();
19518         }
19519         //this.setSize(this.owner.wrap.getSize());
19520         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19521     },
19522
19523     
19524   
19525
19526     /**
19527      * Protected method that will not generally be called directly. If you need/want
19528      * custom HTML cleanup, this is the method you should override.
19529      * @param {String} html The HTML to be cleaned
19530      * return {String} The cleaned HTML
19531      */
19532     cleanHtml : function(html){
19533         html = String(html);
19534         if(html.length > 5){
19535             if(Roo.isSafari){ // strip safari nonsense
19536                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19537             }
19538         }
19539         if(html == '&nbsp;'){
19540             html = '';
19541         }
19542         return html;
19543     },
19544
19545     /**
19546      * HTML Editor -> Textarea
19547      * Protected method that will not generally be called directly. Syncs the contents
19548      * of the editor iframe with the textarea.
19549      */
19550     syncValue : function(){
19551         if(this.initialized){
19552             var bd = (this.doc.body || this.doc.documentElement);
19553             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19554             var html = bd.innerHTML;
19555             if(Roo.isSafari){
19556                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19557                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19558                 if(m && m[1]){
19559                     html = '<div style="'+m[0]+'">' + html + '</div>';
19560                 }
19561             }
19562             html = this.cleanHtml(html);
19563             // fix up the special chars.. normaly like back quotes in word...
19564             // however we do not want to do this with chinese..
19565             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19566                 var cc = b.charCodeAt();
19567                 if (
19568                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19569                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19570                     (cc >= 0xf900 && cc < 0xfb00 )
19571                 ) {
19572                         return b;
19573                 }
19574                 return "&#"+cc+";" 
19575             });
19576             if(this.owner.fireEvent('beforesync', this, html) !== false){
19577                 this.el.dom.value = html;
19578                 this.owner.fireEvent('sync', this, html);
19579             }
19580         }
19581     },
19582
19583     /**
19584      * Protected method that will not generally be called directly. Pushes the value of the textarea
19585      * into the iframe editor.
19586      */
19587     pushValue : function(){
19588         if(this.initialized){
19589             var v = this.el.dom.value.trim();
19590             
19591 //            if(v.length < 1){
19592 //                v = '&#160;';
19593 //            }
19594             
19595             if(this.owner.fireEvent('beforepush', this, v) !== false){
19596                 var d = (this.doc.body || this.doc.documentElement);
19597                 d.innerHTML = v;
19598                 this.cleanUpPaste();
19599                 this.el.dom.value = d.innerHTML;
19600                 this.owner.fireEvent('push', this, v);
19601             }
19602         }
19603     },
19604
19605     // private
19606     deferFocus : function(){
19607         this.focus.defer(10, this);
19608     },
19609
19610     // doc'ed in Field
19611     focus : function(){
19612         if(this.win && !this.sourceEditMode){
19613             this.win.focus();
19614         }else{
19615             this.el.focus();
19616         }
19617     },
19618     
19619     assignDocWin: function()
19620     {
19621         var iframe = this.iframe;
19622         
19623          if(Roo.isIE){
19624             this.doc = iframe.contentWindow.document;
19625             this.win = iframe.contentWindow;
19626         } else {
19627 //            if (!Roo.get(this.frameId)) {
19628 //                return;
19629 //            }
19630 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19631 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19632             
19633             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19634                 return;
19635             }
19636             
19637             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19638             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19639         }
19640     },
19641     
19642     // private
19643     initEditor : function(){
19644         //console.log("INIT EDITOR");
19645         this.assignDocWin();
19646         
19647         
19648         
19649         this.doc.designMode="on";
19650         this.doc.open();
19651         this.doc.write(this.getDocMarkup());
19652         this.doc.close();
19653         
19654         var dbody = (this.doc.body || this.doc.documentElement);
19655         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19656         // this copies styles from the containing element into thsi one..
19657         // not sure why we need all of this..
19658         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19659         
19660         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19661         //ss['background-attachment'] = 'fixed'; // w3c
19662         dbody.bgProperties = 'fixed'; // ie
19663         //Roo.DomHelper.applyStyles(dbody, ss);
19664         Roo.EventManager.on(this.doc, {
19665             //'mousedown': this.onEditorEvent,
19666             'mouseup': this.onEditorEvent,
19667             'dblclick': this.onEditorEvent,
19668             'click': this.onEditorEvent,
19669             'keyup': this.onEditorEvent,
19670             buffer:100,
19671             scope: this
19672         });
19673         if(Roo.isGecko){
19674             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19675         }
19676         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19677             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19678         }
19679         this.initialized = true;
19680
19681         this.owner.fireEvent('initialize', this);
19682         this.pushValue();
19683     },
19684
19685     // private
19686     onDestroy : function(){
19687         
19688         
19689         
19690         if(this.rendered){
19691             
19692             //for (var i =0; i < this.toolbars.length;i++) {
19693             //    // fixme - ask toolbars for heights?
19694             //    this.toolbars[i].onDestroy();
19695            // }
19696             
19697             //this.wrap.dom.innerHTML = '';
19698             //this.wrap.remove();
19699         }
19700     },
19701
19702     // private
19703     onFirstFocus : function(){
19704         
19705         this.assignDocWin();
19706         
19707         
19708         this.activated = true;
19709          
19710     
19711         if(Roo.isGecko){ // prevent silly gecko errors
19712             this.win.focus();
19713             var s = this.win.getSelection();
19714             if(!s.focusNode || s.focusNode.nodeType != 3){
19715                 var r = s.getRangeAt(0);
19716                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19717                 r.collapse(true);
19718                 this.deferFocus();
19719             }
19720             try{
19721                 this.execCmd('useCSS', true);
19722                 this.execCmd('styleWithCSS', false);
19723             }catch(e){}
19724         }
19725         this.owner.fireEvent('activate', this);
19726     },
19727
19728     // private
19729     adjustFont: function(btn){
19730         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19731         //if(Roo.isSafari){ // safari
19732         //    adjust *= 2;
19733        // }
19734         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19735         if(Roo.isSafari){ // safari
19736             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19737             v =  (v < 10) ? 10 : v;
19738             v =  (v > 48) ? 48 : v;
19739             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19740             
19741         }
19742         
19743         
19744         v = Math.max(1, v+adjust);
19745         
19746         this.execCmd('FontSize', v  );
19747     },
19748
19749     onEditorEvent : function(e)
19750     {
19751         this.owner.fireEvent('editorevent', this, e);
19752       //  this.updateToolbar();
19753         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19754     },
19755
19756     insertTag : function(tg)
19757     {
19758         // could be a bit smarter... -> wrap the current selected tRoo..
19759         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19760             
19761             range = this.createRange(this.getSelection());
19762             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19763             wrappingNode.appendChild(range.extractContents());
19764             range.insertNode(wrappingNode);
19765
19766             return;
19767             
19768             
19769             
19770         }
19771         this.execCmd("formatblock",   tg);
19772         
19773     },
19774     
19775     insertText : function(txt)
19776     {
19777         
19778         
19779         var range = this.createRange();
19780         range.deleteContents();
19781                //alert(Sender.getAttribute('label'));
19782                
19783         range.insertNode(this.doc.createTextNode(txt));
19784     } ,
19785     
19786      
19787
19788     /**
19789      * Executes a Midas editor command on the editor document and performs necessary focus and
19790      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19791      * @param {String} cmd The Midas command
19792      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19793      */
19794     relayCmd : function(cmd, value){
19795         this.win.focus();
19796         this.execCmd(cmd, value);
19797         this.owner.fireEvent('editorevent', this);
19798         //this.updateToolbar();
19799         this.owner.deferFocus();
19800     },
19801
19802     /**
19803      * Executes a Midas editor command directly on the editor document.
19804      * For visual commands, you should use {@link #relayCmd} instead.
19805      * <b>This should only be called after the editor is initialized.</b>
19806      * @param {String} cmd The Midas command
19807      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19808      */
19809     execCmd : function(cmd, value){
19810         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19811         this.syncValue();
19812     },
19813  
19814  
19815    
19816     /**
19817      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19818      * to insert tRoo.
19819      * @param {String} text | dom node.. 
19820      */
19821     insertAtCursor : function(text)
19822     {
19823         
19824         
19825         
19826         if(!this.activated){
19827             return;
19828         }
19829         /*
19830         if(Roo.isIE){
19831             this.win.focus();
19832             var r = this.doc.selection.createRange();
19833             if(r){
19834                 r.collapse(true);
19835                 r.pasteHTML(text);
19836                 this.syncValue();
19837                 this.deferFocus();
19838             
19839             }
19840             return;
19841         }
19842         */
19843         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19844             this.win.focus();
19845             
19846             
19847             // from jquery ui (MIT licenced)
19848             var range, node;
19849             var win = this.win;
19850             
19851             if (win.getSelection && win.getSelection().getRangeAt) {
19852                 range = win.getSelection().getRangeAt(0);
19853                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19854                 range.insertNode(node);
19855             } else if (win.document.selection && win.document.selection.createRange) {
19856                 // no firefox support
19857                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19858                 win.document.selection.createRange().pasteHTML(txt);
19859             } else {
19860                 // no firefox support
19861                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19862                 this.execCmd('InsertHTML', txt);
19863             } 
19864             
19865             this.syncValue();
19866             
19867             this.deferFocus();
19868         }
19869     },
19870  // private
19871     mozKeyPress : function(e){
19872         if(e.ctrlKey){
19873             var c = e.getCharCode(), cmd;
19874           
19875             if(c > 0){
19876                 c = String.fromCharCode(c).toLowerCase();
19877                 switch(c){
19878                     case 'b':
19879                         cmd = 'bold';
19880                         break;
19881                     case 'i':
19882                         cmd = 'italic';
19883                         break;
19884                     
19885                     case 'u':
19886                         cmd = 'underline';
19887                         break;
19888                     
19889                     case 'v':
19890                         this.cleanUpPaste.defer(100, this);
19891                         return;
19892                         
19893                 }
19894                 if(cmd){
19895                     this.win.focus();
19896                     this.execCmd(cmd);
19897                     this.deferFocus();
19898                     e.preventDefault();
19899                 }
19900                 
19901             }
19902         }
19903     },
19904
19905     // private
19906     fixKeys : function(){ // load time branching for fastest keydown performance
19907         if(Roo.isIE){
19908             return function(e){
19909                 var k = e.getKey(), r;
19910                 if(k == e.TAB){
19911                     e.stopEvent();
19912                     r = this.doc.selection.createRange();
19913                     if(r){
19914                         r.collapse(true);
19915                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19916                         this.deferFocus();
19917                     }
19918                     return;
19919                 }
19920                 
19921                 if(k == e.ENTER){
19922                     r = this.doc.selection.createRange();
19923                     if(r){
19924                         var target = r.parentElement();
19925                         if(!target || target.tagName.toLowerCase() != 'li'){
19926                             e.stopEvent();
19927                             r.pasteHTML('<br />');
19928                             r.collapse(false);
19929                             r.select();
19930                         }
19931                     }
19932                 }
19933                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19934                     this.cleanUpPaste.defer(100, this);
19935                     return;
19936                 }
19937                 
19938                 
19939             };
19940         }else if(Roo.isOpera){
19941             return function(e){
19942                 var k = e.getKey();
19943                 if(k == e.TAB){
19944                     e.stopEvent();
19945                     this.win.focus();
19946                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19947                     this.deferFocus();
19948                 }
19949                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19950                     this.cleanUpPaste.defer(100, this);
19951                     return;
19952                 }
19953                 
19954             };
19955         }else if(Roo.isSafari){
19956             return function(e){
19957                 var k = e.getKey();
19958                 
19959                 if(k == e.TAB){
19960                     e.stopEvent();
19961                     this.execCmd('InsertText','\t');
19962                     this.deferFocus();
19963                     return;
19964                 }
19965                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19966                     this.cleanUpPaste.defer(100, this);
19967                     return;
19968                 }
19969                 
19970              };
19971         }
19972     }(),
19973     
19974     getAllAncestors: function()
19975     {
19976         var p = this.getSelectedNode();
19977         var a = [];
19978         if (!p) {
19979             a.push(p); // push blank onto stack..
19980             p = this.getParentElement();
19981         }
19982         
19983         
19984         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19985             a.push(p);
19986             p = p.parentNode;
19987         }
19988         a.push(this.doc.body);
19989         return a;
19990     },
19991     lastSel : false,
19992     lastSelNode : false,
19993     
19994     
19995     getSelection : function() 
19996     {
19997         this.assignDocWin();
19998         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19999     },
20000     
20001     getSelectedNode: function() 
20002     {
20003         // this may only work on Gecko!!!
20004         
20005         // should we cache this!!!!
20006         
20007         
20008         
20009          
20010         var range = this.createRange(this.getSelection()).cloneRange();
20011         
20012         if (Roo.isIE) {
20013             var parent = range.parentElement();
20014             while (true) {
20015                 var testRange = range.duplicate();
20016                 testRange.moveToElementText(parent);
20017                 if (testRange.inRange(range)) {
20018                     break;
20019                 }
20020                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20021                     break;
20022                 }
20023                 parent = parent.parentElement;
20024             }
20025             return parent;
20026         }
20027         
20028         // is ancestor a text element.
20029         var ac =  range.commonAncestorContainer;
20030         if (ac.nodeType == 3) {
20031             ac = ac.parentNode;
20032         }
20033         
20034         var ar = ac.childNodes;
20035          
20036         var nodes = [];
20037         var other_nodes = [];
20038         var has_other_nodes = false;
20039         for (var i=0;i<ar.length;i++) {
20040             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20041                 continue;
20042             }
20043             // fullly contained node.
20044             
20045             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20046                 nodes.push(ar[i]);
20047                 continue;
20048             }
20049             
20050             // probably selected..
20051             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20052                 other_nodes.push(ar[i]);
20053                 continue;
20054             }
20055             // outer..
20056             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20057                 continue;
20058             }
20059             
20060             
20061             has_other_nodes = true;
20062         }
20063         if (!nodes.length && other_nodes.length) {
20064             nodes= other_nodes;
20065         }
20066         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20067             return false;
20068         }
20069         
20070         return nodes[0];
20071     },
20072     createRange: function(sel)
20073     {
20074         // this has strange effects when using with 
20075         // top toolbar - not sure if it's a great idea.
20076         //this.editor.contentWindow.focus();
20077         if (typeof sel != "undefined") {
20078             try {
20079                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20080             } catch(e) {
20081                 return this.doc.createRange();
20082             }
20083         } else {
20084             return this.doc.createRange();
20085         }
20086     },
20087     getParentElement: function()
20088     {
20089         
20090         this.assignDocWin();
20091         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20092         
20093         var range = this.createRange(sel);
20094          
20095         try {
20096             var p = range.commonAncestorContainer;
20097             while (p.nodeType == 3) { // text node
20098                 p = p.parentNode;
20099             }
20100             return p;
20101         } catch (e) {
20102             return null;
20103         }
20104     
20105     },
20106     /***
20107      *
20108      * Range intersection.. the hard stuff...
20109      *  '-1' = before
20110      *  '0' = hits..
20111      *  '1' = after.
20112      *         [ -- selected range --- ]
20113      *   [fail]                        [fail]
20114      *
20115      *    basically..
20116      *      if end is before start or  hits it. fail.
20117      *      if start is after end or hits it fail.
20118      *
20119      *   if either hits (but other is outside. - then it's not 
20120      *   
20121      *    
20122      **/
20123     
20124     
20125     // @see http://www.thismuchiknow.co.uk/?p=64.
20126     rangeIntersectsNode : function(range, node)
20127     {
20128         var nodeRange = node.ownerDocument.createRange();
20129         try {
20130             nodeRange.selectNode(node);
20131         } catch (e) {
20132             nodeRange.selectNodeContents(node);
20133         }
20134     
20135         var rangeStartRange = range.cloneRange();
20136         rangeStartRange.collapse(true);
20137     
20138         var rangeEndRange = range.cloneRange();
20139         rangeEndRange.collapse(false);
20140     
20141         var nodeStartRange = nodeRange.cloneRange();
20142         nodeStartRange.collapse(true);
20143     
20144         var nodeEndRange = nodeRange.cloneRange();
20145         nodeEndRange.collapse(false);
20146     
20147         return rangeStartRange.compareBoundaryPoints(
20148                  Range.START_TO_START, nodeEndRange) == -1 &&
20149                rangeEndRange.compareBoundaryPoints(
20150                  Range.START_TO_START, nodeStartRange) == 1;
20151         
20152          
20153     },
20154     rangeCompareNode : function(range, node)
20155     {
20156         var nodeRange = node.ownerDocument.createRange();
20157         try {
20158             nodeRange.selectNode(node);
20159         } catch (e) {
20160             nodeRange.selectNodeContents(node);
20161         }
20162         
20163         
20164         range.collapse(true);
20165     
20166         nodeRange.collapse(true);
20167      
20168         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20169         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20170          
20171         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20172         
20173         var nodeIsBefore   =  ss == 1;
20174         var nodeIsAfter    = ee == -1;
20175         
20176         if (nodeIsBefore && nodeIsAfter) {
20177             return 0; // outer
20178         }
20179         if (!nodeIsBefore && nodeIsAfter) {
20180             return 1; //right trailed.
20181         }
20182         
20183         if (nodeIsBefore && !nodeIsAfter) {
20184             return 2;  // left trailed.
20185         }
20186         // fully contined.
20187         return 3;
20188     },
20189
20190     // private? - in a new class?
20191     cleanUpPaste :  function()
20192     {
20193         // cleans up the whole document..
20194         Roo.log('cleanuppaste');
20195         
20196         this.cleanUpChildren(this.doc.body);
20197         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20198         if (clean != this.doc.body.innerHTML) {
20199             this.doc.body.innerHTML = clean;
20200         }
20201         
20202     },
20203     
20204     cleanWordChars : function(input) {// change the chars to hex code
20205         var he = Roo.HtmlEditorCore;
20206         
20207         var output = input;
20208         Roo.each(he.swapCodes, function(sw) { 
20209             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20210             
20211             output = output.replace(swapper, sw[1]);
20212         });
20213         
20214         return output;
20215     },
20216     
20217     
20218     cleanUpChildren : function (n)
20219     {
20220         if (!n.childNodes.length) {
20221             return;
20222         }
20223         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20224            this.cleanUpChild(n.childNodes[i]);
20225         }
20226     },
20227     
20228     
20229         
20230     
20231     cleanUpChild : function (node)
20232     {
20233         var ed = this;
20234         //console.log(node);
20235         if (node.nodeName == "#text") {
20236             // clean up silly Windows -- stuff?
20237             return; 
20238         }
20239         if (node.nodeName == "#comment") {
20240             node.parentNode.removeChild(node);
20241             // clean up silly Windows -- stuff?
20242             return; 
20243         }
20244         var lcname = node.tagName.toLowerCase();
20245         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20246         // whitelist of tags..
20247         
20248         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20249             // remove node.
20250             node.parentNode.removeChild(node);
20251             return;
20252             
20253         }
20254         
20255         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20256         
20257         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20258         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20259         
20260         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20261         //    remove_keep_children = true;
20262         //}
20263         
20264         if (remove_keep_children) {
20265             this.cleanUpChildren(node);
20266             // inserts everything just before this node...
20267             while (node.childNodes.length) {
20268                 var cn = node.childNodes[0];
20269                 node.removeChild(cn);
20270                 node.parentNode.insertBefore(cn, node);
20271             }
20272             node.parentNode.removeChild(node);
20273             return;
20274         }
20275         
20276         if (!node.attributes || !node.attributes.length) {
20277             this.cleanUpChildren(node);
20278             return;
20279         }
20280         
20281         function cleanAttr(n,v)
20282         {
20283             
20284             if (v.match(/^\./) || v.match(/^\//)) {
20285                 return;
20286             }
20287             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20288                 return;
20289             }
20290             if (v.match(/^#/)) {
20291                 return;
20292             }
20293 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20294             node.removeAttribute(n);
20295             
20296         }
20297         
20298         var cwhite = this.cwhite;
20299         var cblack = this.cblack;
20300             
20301         function cleanStyle(n,v)
20302         {
20303             if (v.match(/expression/)) { //XSS?? should we even bother..
20304                 node.removeAttribute(n);
20305                 return;
20306             }
20307             
20308             var parts = v.split(/;/);
20309             var clean = [];
20310             
20311             Roo.each(parts, function(p) {
20312                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20313                 if (!p.length) {
20314                     return true;
20315                 }
20316                 var l = p.split(':').shift().replace(/\s+/g,'');
20317                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20318                 
20319                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20320 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20321                     //node.removeAttribute(n);
20322                     return true;
20323                 }
20324                 //Roo.log()
20325                 // only allow 'c whitelisted system attributes'
20326                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20327 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20328                     //node.removeAttribute(n);
20329                     return true;
20330                 }
20331                 
20332                 
20333                  
20334                 
20335                 clean.push(p);
20336                 return true;
20337             });
20338             if (clean.length) { 
20339                 node.setAttribute(n, clean.join(';'));
20340             } else {
20341                 node.removeAttribute(n);
20342             }
20343             
20344         }
20345         
20346         
20347         for (var i = node.attributes.length-1; i > -1 ; i--) {
20348             var a = node.attributes[i];
20349             //console.log(a);
20350             
20351             if (a.name.toLowerCase().substr(0,2)=='on')  {
20352                 node.removeAttribute(a.name);
20353                 continue;
20354             }
20355             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20356                 node.removeAttribute(a.name);
20357                 continue;
20358             }
20359             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20360                 cleanAttr(a.name,a.value); // fixme..
20361                 continue;
20362             }
20363             if (a.name == 'style') {
20364                 cleanStyle(a.name,a.value);
20365                 continue;
20366             }
20367             /// clean up MS crap..
20368             // tecnically this should be a list of valid class'es..
20369             
20370             
20371             if (a.name == 'class') {
20372                 if (a.value.match(/^Mso/)) {
20373                     node.className = '';
20374                 }
20375                 
20376                 if (a.value.match(/body/)) {
20377                     node.className = '';
20378                 }
20379                 continue;
20380             }
20381             
20382             // style cleanup!?
20383             // class cleanup?
20384             
20385         }
20386         
20387         
20388         this.cleanUpChildren(node);
20389         
20390         
20391     },
20392     
20393     /**
20394      * Clean up MS wordisms...
20395      */
20396     cleanWord : function(node)
20397     {
20398         
20399         
20400         if (!node) {
20401             this.cleanWord(this.doc.body);
20402             return;
20403         }
20404         if (node.nodeName == "#text") {
20405             // clean up silly Windows -- stuff?
20406             return; 
20407         }
20408         if (node.nodeName == "#comment") {
20409             node.parentNode.removeChild(node);
20410             // clean up silly Windows -- stuff?
20411             return; 
20412         }
20413         
20414         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20415             node.parentNode.removeChild(node);
20416             return;
20417         }
20418         
20419         // remove - but keep children..
20420         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20421             while (node.childNodes.length) {
20422                 var cn = node.childNodes[0];
20423                 node.removeChild(cn);
20424                 node.parentNode.insertBefore(cn, node);
20425             }
20426             node.parentNode.removeChild(node);
20427             this.iterateChildren(node, this.cleanWord);
20428             return;
20429         }
20430         // clean styles
20431         if (node.className.length) {
20432             
20433             var cn = node.className.split(/\W+/);
20434             var cna = [];
20435             Roo.each(cn, function(cls) {
20436                 if (cls.match(/Mso[a-zA-Z]+/)) {
20437                     return;
20438                 }
20439                 cna.push(cls);
20440             });
20441             node.className = cna.length ? cna.join(' ') : '';
20442             if (!cna.length) {
20443                 node.removeAttribute("class");
20444             }
20445         }
20446         
20447         if (node.hasAttribute("lang")) {
20448             node.removeAttribute("lang");
20449         }
20450         
20451         if (node.hasAttribute("style")) {
20452             
20453             var styles = node.getAttribute("style").split(";");
20454             var nstyle = [];
20455             Roo.each(styles, function(s) {
20456                 if (!s.match(/:/)) {
20457                     return;
20458                 }
20459                 var kv = s.split(":");
20460                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20461                     return;
20462                 }
20463                 // what ever is left... we allow.
20464                 nstyle.push(s);
20465             });
20466             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20467             if (!nstyle.length) {
20468                 node.removeAttribute('style');
20469             }
20470         }
20471         this.iterateChildren(node, this.cleanWord);
20472         
20473         
20474         
20475     },
20476     /**
20477      * iterateChildren of a Node, calling fn each time, using this as the scole..
20478      * @param {DomNode} node node to iterate children of.
20479      * @param {Function} fn method of this class to call on each item.
20480      */
20481     iterateChildren : function(node, fn)
20482     {
20483         if (!node.childNodes.length) {
20484                 return;
20485         }
20486         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20487            fn.call(this, node.childNodes[i])
20488         }
20489     },
20490     
20491     
20492     /**
20493      * cleanTableWidths.
20494      *
20495      * Quite often pasting from word etc.. results in tables with column and widths.
20496      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20497      *
20498      */
20499     cleanTableWidths : function(node)
20500     {
20501          
20502          
20503         if (!node) {
20504             this.cleanTableWidths(this.doc.body);
20505             return;
20506         }
20507         
20508         // ignore list...
20509         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20510             return; 
20511         }
20512         Roo.log(node.tagName);
20513         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20514             this.iterateChildren(node, this.cleanTableWidths);
20515             return;
20516         }
20517         if (node.hasAttribute('width')) {
20518             node.removeAttribute('width');
20519         }
20520         
20521          
20522         if (node.hasAttribute("style")) {
20523             // pretty basic...
20524             
20525             var styles = node.getAttribute("style").split(";");
20526             var nstyle = [];
20527             Roo.each(styles, function(s) {
20528                 if (!s.match(/:/)) {
20529                     return;
20530                 }
20531                 var kv = s.split(":");
20532                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20533                     return;
20534                 }
20535                 // what ever is left... we allow.
20536                 nstyle.push(s);
20537             });
20538             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20539             if (!nstyle.length) {
20540                 node.removeAttribute('style');
20541             }
20542         }
20543         
20544         this.iterateChildren(node, this.cleanTableWidths);
20545         
20546         
20547     },
20548     
20549     
20550     
20551     
20552     domToHTML : function(currentElement, depth, nopadtext) {
20553         
20554         depth = depth || 0;
20555         nopadtext = nopadtext || false;
20556     
20557         if (!currentElement) {
20558             return this.domToHTML(this.doc.body);
20559         }
20560         
20561         //Roo.log(currentElement);
20562         var j;
20563         var allText = false;
20564         var nodeName = currentElement.nodeName;
20565         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20566         
20567         if  (nodeName == '#text') {
20568             
20569             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20570         }
20571         
20572         
20573         var ret = '';
20574         if (nodeName != 'BODY') {
20575              
20576             var i = 0;
20577             // Prints the node tagName, such as <A>, <IMG>, etc
20578             if (tagName) {
20579                 var attr = [];
20580                 for(i = 0; i < currentElement.attributes.length;i++) {
20581                     // quoting?
20582                     var aname = currentElement.attributes.item(i).name;
20583                     if (!currentElement.attributes.item(i).value.length) {
20584                         continue;
20585                     }
20586                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20587                 }
20588                 
20589                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20590             } 
20591             else {
20592                 
20593                 // eack
20594             }
20595         } else {
20596             tagName = false;
20597         }
20598         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20599             return ret;
20600         }
20601         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20602             nopadtext = true;
20603         }
20604         
20605         
20606         // Traverse the tree
20607         i = 0;
20608         var currentElementChild = currentElement.childNodes.item(i);
20609         var allText = true;
20610         var innerHTML  = '';
20611         lastnode = '';
20612         while (currentElementChild) {
20613             // Formatting code (indent the tree so it looks nice on the screen)
20614             var nopad = nopadtext;
20615             if (lastnode == 'SPAN') {
20616                 nopad  = true;
20617             }
20618             // text
20619             if  (currentElementChild.nodeName == '#text') {
20620                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20621                 toadd = nopadtext ? toadd : toadd.trim();
20622                 if (!nopad && toadd.length > 80) {
20623                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20624                 }
20625                 innerHTML  += toadd;
20626                 
20627                 i++;
20628                 currentElementChild = currentElement.childNodes.item(i);
20629                 lastNode = '';
20630                 continue;
20631             }
20632             allText = false;
20633             
20634             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20635                 
20636             // Recursively traverse the tree structure of the child node
20637             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20638             lastnode = currentElementChild.nodeName;
20639             i++;
20640             currentElementChild=currentElement.childNodes.item(i);
20641         }
20642         
20643         ret += innerHTML;
20644         
20645         if (!allText) {
20646                 // The remaining code is mostly for formatting the tree
20647             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20648         }
20649         
20650         
20651         if (tagName) {
20652             ret+= "</"+tagName+">";
20653         }
20654         return ret;
20655         
20656     },
20657         
20658     applyBlacklists : function()
20659     {
20660         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20661         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20662         
20663         this.white = [];
20664         this.black = [];
20665         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20666             if (b.indexOf(tag) > -1) {
20667                 return;
20668             }
20669             this.white.push(tag);
20670             
20671         }, this);
20672         
20673         Roo.each(w, function(tag) {
20674             if (b.indexOf(tag) > -1) {
20675                 return;
20676             }
20677             if (this.white.indexOf(tag) > -1) {
20678                 return;
20679             }
20680             this.white.push(tag);
20681             
20682         }, this);
20683         
20684         
20685         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20686             if (w.indexOf(tag) > -1) {
20687                 return;
20688             }
20689             this.black.push(tag);
20690             
20691         }, this);
20692         
20693         Roo.each(b, function(tag) {
20694             if (w.indexOf(tag) > -1) {
20695                 return;
20696             }
20697             if (this.black.indexOf(tag) > -1) {
20698                 return;
20699             }
20700             this.black.push(tag);
20701             
20702         }, this);
20703         
20704         
20705         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20706         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20707         
20708         this.cwhite = [];
20709         this.cblack = [];
20710         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20711             if (b.indexOf(tag) > -1) {
20712                 return;
20713             }
20714             this.cwhite.push(tag);
20715             
20716         }, this);
20717         
20718         Roo.each(w, function(tag) {
20719             if (b.indexOf(tag) > -1) {
20720                 return;
20721             }
20722             if (this.cwhite.indexOf(tag) > -1) {
20723                 return;
20724             }
20725             this.cwhite.push(tag);
20726             
20727         }, this);
20728         
20729         
20730         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20731             if (w.indexOf(tag) > -1) {
20732                 return;
20733             }
20734             this.cblack.push(tag);
20735             
20736         }, this);
20737         
20738         Roo.each(b, function(tag) {
20739             if (w.indexOf(tag) > -1) {
20740                 return;
20741             }
20742             if (this.cblack.indexOf(tag) > -1) {
20743                 return;
20744             }
20745             this.cblack.push(tag);
20746             
20747         }, this);
20748     },
20749     
20750     setStylesheets : function(stylesheets)
20751     {
20752         if(typeof(stylesheets) == 'string'){
20753             Roo.get(this.iframe.contentDocument.head).createChild({
20754                 tag : 'link',
20755                 rel : 'stylesheet',
20756                 type : 'text/css',
20757                 href : stylesheets
20758             });
20759             
20760             return;
20761         }
20762         var _this = this;
20763      
20764         Roo.each(stylesheets, function(s) {
20765             if(!s.length){
20766                 return;
20767             }
20768             
20769             Roo.get(_this.iframe.contentDocument.head).createChild({
20770                 tag : 'link',
20771                 rel : 'stylesheet',
20772                 type : 'text/css',
20773                 href : s
20774             });
20775         });
20776
20777         
20778     },
20779     
20780     removeStylesheets : function()
20781     {
20782         var _this = this;
20783         
20784         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20785             s.remove();
20786         });
20787     }
20788     
20789     // hide stuff that is not compatible
20790     /**
20791      * @event blur
20792      * @hide
20793      */
20794     /**
20795      * @event change
20796      * @hide
20797      */
20798     /**
20799      * @event focus
20800      * @hide
20801      */
20802     /**
20803      * @event specialkey
20804      * @hide
20805      */
20806     /**
20807      * @cfg {String} fieldClass @hide
20808      */
20809     /**
20810      * @cfg {String} focusClass @hide
20811      */
20812     /**
20813      * @cfg {String} autoCreate @hide
20814      */
20815     /**
20816      * @cfg {String} inputType @hide
20817      */
20818     /**
20819      * @cfg {String} invalidClass @hide
20820      */
20821     /**
20822      * @cfg {String} invalidText @hide
20823      */
20824     /**
20825      * @cfg {String} msgFx @hide
20826      */
20827     /**
20828      * @cfg {String} validateOnBlur @hide
20829      */
20830 });
20831
20832 Roo.HtmlEditorCore.white = [
20833         'area', 'br', 'img', 'input', 'hr', 'wbr',
20834         
20835        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20836        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20837        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20838        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20839        'table',   'ul',         'xmp', 
20840        
20841        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20842       'thead',   'tr', 
20843      
20844       'dir', 'menu', 'ol', 'ul', 'dl',
20845        
20846       'embed',  'object'
20847 ];
20848
20849
20850 Roo.HtmlEditorCore.black = [
20851     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20852         'applet', // 
20853         'base',   'basefont', 'bgsound', 'blink',  'body', 
20854         'frame',  'frameset', 'head',    'html',   'ilayer', 
20855         'iframe', 'layer',  'link',     'meta',    'object',   
20856         'script', 'style' ,'title',  'xml' // clean later..
20857 ];
20858 Roo.HtmlEditorCore.clean = [
20859     'script', 'style', 'title', 'xml'
20860 ];
20861 Roo.HtmlEditorCore.remove = [
20862     'font'
20863 ];
20864 // attributes..
20865
20866 Roo.HtmlEditorCore.ablack = [
20867     'on'
20868 ];
20869     
20870 Roo.HtmlEditorCore.aclean = [ 
20871     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20872 ];
20873
20874 // protocols..
20875 Roo.HtmlEditorCore.pwhite= [
20876         'http',  'https',  'mailto'
20877 ];
20878
20879 // white listed style attributes.
20880 Roo.HtmlEditorCore.cwhite= [
20881       //  'text-align', /// default is to allow most things..
20882       
20883          
20884 //        'font-size'//??
20885 ];
20886
20887 // black listed style attributes.
20888 Roo.HtmlEditorCore.cblack= [
20889       //  'font-size' -- this can be set by the project 
20890 ];
20891
20892
20893 Roo.HtmlEditorCore.swapCodes   =[ 
20894     [    8211, "--" ], 
20895     [    8212, "--" ], 
20896     [    8216,  "'" ],  
20897     [    8217, "'" ],  
20898     [    8220, '"' ],  
20899     [    8221, '"' ],  
20900     [    8226, "*" ],  
20901     [    8230, "..." ]
20902 ]; 
20903
20904     /*
20905  * - LGPL
20906  *
20907  * HtmlEditor
20908  * 
20909  */
20910
20911 /**
20912  * @class Roo.bootstrap.HtmlEditor
20913  * @extends Roo.bootstrap.TextArea
20914  * Bootstrap HtmlEditor class
20915
20916  * @constructor
20917  * Create a new HtmlEditor
20918  * @param {Object} config The config object
20919  */
20920
20921 Roo.bootstrap.HtmlEditor = function(config){
20922     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20923     if (!this.toolbars) {
20924         this.toolbars = [];
20925     }
20926     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20927     this.addEvents({
20928             /**
20929              * @event initialize
20930              * Fires when the editor is fully initialized (including the iframe)
20931              * @param {HtmlEditor} this
20932              */
20933             initialize: true,
20934             /**
20935              * @event activate
20936              * Fires when the editor is first receives the focus. Any insertion must wait
20937              * until after this event.
20938              * @param {HtmlEditor} this
20939              */
20940             activate: true,
20941              /**
20942              * @event beforesync
20943              * Fires before the textarea is updated with content from the editor iframe. Return false
20944              * to cancel the sync.
20945              * @param {HtmlEditor} this
20946              * @param {String} html
20947              */
20948             beforesync: true,
20949              /**
20950              * @event beforepush
20951              * Fires before the iframe editor is updated with content from the textarea. Return false
20952              * to cancel the push.
20953              * @param {HtmlEditor} this
20954              * @param {String} html
20955              */
20956             beforepush: true,
20957              /**
20958              * @event sync
20959              * Fires when the textarea is updated with content from the editor iframe.
20960              * @param {HtmlEditor} this
20961              * @param {String} html
20962              */
20963             sync: true,
20964              /**
20965              * @event push
20966              * Fires when the iframe editor is updated with content from the textarea.
20967              * @param {HtmlEditor} this
20968              * @param {String} html
20969              */
20970             push: true,
20971              /**
20972              * @event editmodechange
20973              * Fires when the editor switches edit modes
20974              * @param {HtmlEditor} this
20975              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20976              */
20977             editmodechange: true,
20978             /**
20979              * @event editorevent
20980              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20981              * @param {HtmlEditor} this
20982              */
20983             editorevent: true,
20984             /**
20985              * @event firstfocus
20986              * Fires when on first focus - needed by toolbars..
20987              * @param {HtmlEditor} this
20988              */
20989             firstfocus: true,
20990             /**
20991              * @event autosave
20992              * Auto save the htmlEditor value as a file into Events
20993              * @param {HtmlEditor} this
20994              */
20995             autosave: true,
20996             /**
20997              * @event savedpreview
20998              * preview the saved version of htmlEditor
20999              * @param {HtmlEditor} this
21000              */
21001             savedpreview: true
21002         });
21003 };
21004
21005
21006 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21007     
21008     
21009       /**
21010      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21011      */
21012     toolbars : false,
21013    
21014      /**
21015      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21016      *                        Roo.resizable.
21017      */
21018     resizable : false,
21019      /**
21020      * @cfg {Number} height (in pixels)
21021      */   
21022     height: 300,
21023    /**
21024      * @cfg {Number} width (in pixels)
21025      */   
21026     width: false,
21027     
21028     /**
21029      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21030      * 
21031      */
21032     stylesheets: false,
21033     
21034     // id of frame..
21035     frameId: false,
21036     
21037     // private properties
21038     validationEvent : false,
21039     deferHeight: true,
21040     initialized : false,
21041     activated : false,
21042     
21043     onFocus : Roo.emptyFn,
21044     iframePad:3,
21045     hideMode:'offsets',
21046     
21047     
21048     tbContainer : false,
21049     
21050     toolbarContainer :function() {
21051         return this.wrap.select('.x-html-editor-tb',true).first();
21052     },
21053
21054     /**
21055      * Protected method that will not generally be called directly. It
21056      * is called when the editor creates its toolbar. Override this method if you need to
21057      * add custom toolbar buttons.
21058      * @param {HtmlEditor} editor
21059      */
21060     createToolbar : function(){
21061         
21062         Roo.log("create toolbars");
21063         
21064         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21065         this.toolbars[0].render(this.toolbarContainer());
21066         
21067         return;
21068         
21069 //        if (!editor.toolbars || !editor.toolbars.length) {
21070 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21071 //        }
21072 //        
21073 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21074 //            editor.toolbars[i] = Roo.factory(
21075 //                    typeof(editor.toolbars[i]) == 'string' ?
21076 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21077 //                Roo.bootstrap.HtmlEditor);
21078 //            editor.toolbars[i].init(editor);
21079 //        }
21080     },
21081
21082      
21083     // private
21084     onRender : function(ct, position)
21085     {
21086        // Roo.log("Call onRender: " + this.xtype);
21087         var _t = this;
21088         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21089       
21090         this.wrap = this.inputEl().wrap({
21091             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21092         });
21093         
21094         this.editorcore.onRender(ct, position);
21095          
21096         if (this.resizable) {
21097             this.resizeEl = new Roo.Resizable(this.wrap, {
21098                 pinned : true,
21099                 wrap: true,
21100                 dynamic : true,
21101                 minHeight : this.height,
21102                 height: this.height,
21103                 handles : this.resizable,
21104                 width: this.width,
21105                 listeners : {
21106                     resize : function(r, w, h) {
21107                         _t.onResize(w,h); // -something
21108                     }
21109                 }
21110             });
21111             
21112         }
21113         this.createToolbar(this);
21114        
21115         
21116         if(!this.width && this.resizable){
21117             this.setSize(this.wrap.getSize());
21118         }
21119         if (this.resizeEl) {
21120             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21121             // should trigger onReize..
21122         }
21123         
21124     },
21125
21126     // private
21127     onResize : function(w, h)
21128     {
21129         Roo.log('resize: ' +w + ',' + h );
21130         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21131         var ew = false;
21132         var eh = false;
21133         
21134         if(this.inputEl() ){
21135             if(typeof w == 'number'){
21136                 var aw = w - this.wrap.getFrameWidth('lr');
21137                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21138                 ew = aw;
21139             }
21140             if(typeof h == 'number'){
21141                  var tbh = -11;  // fixme it needs to tool bar size!
21142                 for (var i =0; i < this.toolbars.length;i++) {
21143                     // fixme - ask toolbars for heights?
21144                     tbh += this.toolbars[i].el.getHeight();
21145                     //if (this.toolbars[i].footer) {
21146                     //    tbh += this.toolbars[i].footer.el.getHeight();
21147                     //}
21148                 }
21149               
21150                 
21151                 
21152                 
21153                 
21154                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21155                 ah -= 5; // knock a few pixes off for look..
21156                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21157                 var eh = ah;
21158             }
21159         }
21160         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21161         this.editorcore.onResize(ew,eh);
21162         
21163     },
21164
21165     /**
21166      * Toggles the editor between standard and source edit mode.
21167      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21168      */
21169     toggleSourceEdit : function(sourceEditMode)
21170     {
21171         this.editorcore.toggleSourceEdit(sourceEditMode);
21172         
21173         if(this.editorcore.sourceEditMode){
21174             Roo.log('editor - showing textarea');
21175             
21176 //            Roo.log('in');
21177 //            Roo.log(this.syncValue());
21178             this.syncValue();
21179             this.inputEl().removeClass(['hide', 'x-hidden']);
21180             this.inputEl().dom.removeAttribute('tabIndex');
21181             this.inputEl().focus();
21182         }else{
21183             Roo.log('editor - hiding textarea');
21184 //            Roo.log('out')
21185 //            Roo.log(this.pushValue()); 
21186             this.pushValue();
21187             
21188             this.inputEl().addClass(['hide', 'x-hidden']);
21189             this.inputEl().dom.setAttribute('tabIndex', -1);
21190             //this.deferFocus();
21191         }
21192          
21193         if(this.resizable){
21194             this.setSize(this.wrap.getSize());
21195         }
21196         
21197         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21198     },
21199  
21200     // private (for BoxComponent)
21201     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21202
21203     // private (for BoxComponent)
21204     getResizeEl : function(){
21205         return this.wrap;
21206     },
21207
21208     // private (for BoxComponent)
21209     getPositionEl : function(){
21210         return this.wrap;
21211     },
21212
21213     // private
21214     initEvents : function(){
21215         this.originalValue = this.getValue();
21216     },
21217
21218 //    /**
21219 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21220 //     * @method
21221 //     */
21222 //    markInvalid : Roo.emptyFn,
21223 //    /**
21224 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21225 //     * @method
21226 //     */
21227 //    clearInvalid : Roo.emptyFn,
21228
21229     setValue : function(v){
21230         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21231         this.editorcore.pushValue();
21232     },
21233
21234      
21235     // private
21236     deferFocus : function(){
21237         this.focus.defer(10, this);
21238     },
21239
21240     // doc'ed in Field
21241     focus : function(){
21242         this.editorcore.focus();
21243         
21244     },
21245       
21246
21247     // private
21248     onDestroy : function(){
21249         
21250         
21251         
21252         if(this.rendered){
21253             
21254             for (var i =0; i < this.toolbars.length;i++) {
21255                 // fixme - ask toolbars for heights?
21256                 this.toolbars[i].onDestroy();
21257             }
21258             
21259             this.wrap.dom.innerHTML = '';
21260             this.wrap.remove();
21261         }
21262     },
21263
21264     // private
21265     onFirstFocus : function(){
21266         //Roo.log("onFirstFocus");
21267         this.editorcore.onFirstFocus();
21268          for (var i =0; i < this.toolbars.length;i++) {
21269             this.toolbars[i].onFirstFocus();
21270         }
21271         
21272     },
21273     
21274     // private
21275     syncValue : function()
21276     {   
21277         this.editorcore.syncValue();
21278     },
21279     
21280     pushValue : function()
21281     {   
21282         this.editorcore.pushValue();
21283     }
21284      
21285     
21286     // hide stuff that is not compatible
21287     /**
21288      * @event blur
21289      * @hide
21290      */
21291     /**
21292      * @event change
21293      * @hide
21294      */
21295     /**
21296      * @event focus
21297      * @hide
21298      */
21299     /**
21300      * @event specialkey
21301      * @hide
21302      */
21303     /**
21304      * @cfg {String} fieldClass @hide
21305      */
21306     /**
21307      * @cfg {String} focusClass @hide
21308      */
21309     /**
21310      * @cfg {String} autoCreate @hide
21311      */
21312     /**
21313      * @cfg {String} inputType @hide
21314      */
21315     /**
21316      * @cfg {String} invalidClass @hide
21317      */
21318     /**
21319      * @cfg {String} invalidText @hide
21320      */
21321     /**
21322      * @cfg {String} msgFx @hide
21323      */
21324     /**
21325      * @cfg {String} validateOnBlur @hide
21326      */
21327 });
21328  
21329     
21330    
21331    
21332    
21333       
21334 Roo.namespace('Roo.bootstrap.htmleditor');
21335 /**
21336  * @class Roo.bootstrap.HtmlEditorToolbar1
21337  * Basic Toolbar
21338  * 
21339  * Usage:
21340  *
21341  new Roo.bootstrap.HtmlEditor({
21342     ....
21343     toolbars : [
21344         new Roo.bootstrap.HtmlEditorToolbar1({
21345             disable : { fonts: 1 , format: 1, ..., ... , ...],
21346             btns : [ .... ]
21347         })
21348     }
21349      
21350  * 
21351  * @cfg {Object} disable List of elements to disable..
21352  * @cfg {Array} btns List of additional buttons.
21353  * 
21354  * 
21355  * NEEDS Extra CSS? 
21356  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21357  */
21358  
21359 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21360 {
21361     
21362     Roo.apply(this, config);
21363     
21364     // default disabled, based on 'good practice'..
21365     this.disable = this.disable || {};
21366     Roo.applyIf(this.disable, {
21367         fontSize : true,
21368         colors : true,
21369         specialElements : true
21370     });
21371     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21372     
21373     this.editor = config.editor;
21374     this.editorcore = config.editor.editorcore;
21375     
21376     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21377     
21378     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21379     // dont call parent... till later.
21380 }
21381 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21382      
21383     bar : true,
21384     
21385     editor : false,
21386     editorcore : false,
21387     
21388     
21389     formats : [
21390         "p" ,  
21391         "h1","h2","h3","h4","h5","h6", 
21392         "pre", "code", 
21393         "abbr", "acronym", "address", "cite", "samp", "var",
21394         'div','span'
21395     ],
21396     
21397     onRender : function(ct, position)
21398     {
21399        // Roo.log("Call onRender: " + this.xtype);
21400         
21401        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21402        Roo.log(this.el);
21403        this.el.dom.style.marginBottom = '0';
21404        var _this = this;
21405        var editorcore = this.editorcore;
21406        var editor= this.editor;
21407        
21408        var children = [];
21409        var btn = function(id,cmd , toggle, handler){
21410        
21411             var  event = toggle ? 'toggle' : 'click';
21412        
21413             var a = {
21414                 size : 'sm',
21415                 xtype: 'Button',
21416                 xns: Roo.bootstrap,
21417                 glyphicon : id,
21418                 cmd : id || cmd,
21419                 enableToggle:toggle !== false,
21420                 //html : 'submit'
21421                 pressed : toggle ? false : null,
21422                 listeners : {}
21423             };
21424             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21425                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21426             };
21427             children.push(a);
21428             return a;
21429        }
21430         
21431         var style = {
21432                 xtype: 'Button',
21433                 size : 'sm',
21434                 xns: Roo.bootstrap,
21435                 glyphicon : 'font',
21436                 //html : 'submit'
21437                 menu : {
21438                     xtype: 'Menu',
21439                     xns: Roo.bootstrap,
21440                     items:  []
21441                 }
21442         };
21443         Roo.each(this.formats, function(f) {
21444             style.menu.items.push({
21445                 xtype :'MenuItem',
21446                 xns: Roo.bootstrap,
21447                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21448                 tagname : f,
21449                 listeners : {
21450                     click : function()
21451                     {
21452                         editorcore.insertTag(this.tagname);
21453                         editor.focus();
21454                     }
21455                 }
21456                 
21457             });
21458         });
21459          children.push(style);   
21460             
21461             
21462         btn('bold',false,true);
21463         btn('italic',false,true);
21464         btn('align-left', 'justifyleft',true);
21465         btn('align-center', 'justifycenter',true);
21466         btn('align-right' , 'justifyright',true);
21467         btn('link', false, false, function(btn) {
21468             //Roo.log("create link?");
21469             var url = prompt(this.createLinkText, this.defaultLinkValue);
21470             if(url && url != 'http:/'+'/'){
21471                 this.editorcore.relayCmd('createlink', url);
21472             }
21473         }),
21474         btn('list','insertunorderedlist',true);
21475         btn('pencil', false,true, function(btn){
21476                 Roo.log(this);
21477                 
21478                 this.toggleSourceEdit(btn.pressed);
21479         });
21480         /*
21481         var cog = {
21482                 xtype: 'Button',
21483                 size : 'sm',
21484                 xns: Roo.bootstrap,
21485                 glyphicon : 'cog',
21486                 //html : 'submit'
21487                 menu : {
21488                     xtype: 'Menu',
21489                     xns: Roo.bootstrap,
21490                     items:  []
21491                 }
21492         };
21493         
21494         cog.menu.items.push({
21495             xtype :'MenuItem',
21496             xns: Roo.bootstrap,
21497             html : Clean styles,
21498             tagname : f,
21499             listeners : {
21500                 click : function()
21501                 {
21502                     editorcore.insertTag(this.tagname);
21503                     editor.focus();
21504                 }
21505             }
21506             
21507         });
21508        */
21509         
21510          
21511        this.xtype = 'NavSimplebar';
21512         
21513         for(var i=0;i< children.length;i++) {
21514             
21515             this.buttons.add(this.addxtypeChild(children[i]));
21516             
21517         }
21518         
21519         editor.on('editorevent', this.updateToolbar, this);
21520     },
21521     onBtnClick : function(id)
21522     {
21523        this.editorcore.relayCmd(id);
21524        this.editorcore.focus();
21525     },
21526     
21527     /**
21528      * Protected method that will not generally be called directly. It triggers
21529      * a toolbar update by reading the markup state of the current selection in the editor.
21530      */
21531     updateToolbar: function(){
21532
21533         if(!this.editorcore.activated){
21534             this.editor.onFirstFocus(); // is this neeed?
21535             return;
21536         }
21537
21538         var btns = this.buttons; 
21539         var doc = this.editorcore.doc;
21540         btns.get('bold').setActive(doc.queryCommandState('bold'));
21541         btns.get('italic').setActive(doc.queryCommandState('italic'));
21542         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21543         
21544         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21545         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21546         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21547         
21548         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21549         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21550          /*
21551         
21552         var ans = this.editorcore.getAllAncestors();
21553         if (this.formatCombo) {
21554             
21555             
21556             var store = this.formatCombo.store;
21557             this.formatCombo.setValue("");
21558             for (var i =0; i < ans.length;i++) {
21559                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21560                     // select it..
21561                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21562                     break;
21563                 }
21564             }
21565         }
21566         
21567         
21568         
21569         // hides menus... - so this cant be on a menu...
21570         Roo.bootstrap.MenuMgr.hideAll();
21571         */
21572         Roo.bootstrap.MenuMgr.hideAll();
21573         //this.editorsyncValue();
21574     },
21575     onFirstFocus: function() {
21576         this.buttons.each(function(item){
21577            item.enable();
21578         });
21579     },
21580     toggleSourceEdit : function(sourceEditMode){
21581         
21582           
21583         if(sourceEditMode){
21584             Roo.log("disabling buttons");
21585            this.buttons.each( function(item){
21586                 if(item.cmd != 'pencil'){
21587                     item.disable();
21588                 }
21589             });
21590           
21591         }else{
21592             Roo.log("enabling buttons");
21593             if(this.editorcore.initialized){
21594                 this.buttons.each( function(item){
21595                     item.enable();
21596                 });
21597             }
21598             
21599         }
21600         Roo.log("calling toggole on editor");
21601         // tell the editor that it's been pressed..
21602         this.editor.toggleSourceEdit(sourceEditMode);
21603        
21604     }
21605 });
21606
21607
21608
21609
21610
21611 /**
21612  * @class Roo.bootstrap.Table.AbstractSelectionModel
21613  * @extends Roo.util.Observable
21614  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21615  * implemented by descendant classes.  This class should not be directly instantiated.
21616  * @constructor
21617  */
21618 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21619     this.locked = false;
21620     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21621 };
21622
21623
21624 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21625     /** @ignore Called by the grid automatically. Do not call directly. */
21626     init : function(grid){
21627         this.grid = grid;
21628         this.initEvents();
21629     },
21630
21631     /**
21632      * Locks the selections.
21633      */
21634     lock : function(){
21635         this.locked = true;
21636     },
21637
21638     /**
21639      * Unlocks the selections.
21640      */
21641     unlock : function(){
21642         this.locked = false;
21643     },
21644
21645     /**
21646      * Returns true if the selections are locked.
21647      * @return {Boolean}
21648      */
21649     isLocked : function(){
21650         return this.locked;
21651     }
21652 });
21653 /**
21654  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21655  * @class Roo.bootstrap.Table.RowSelectionModel
21656  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21657  * It supports multiple selections and keyboard selection/navigation. 
21658  * @constructor
21659  * @param {Object} config
21660  */
21661
21662 Roo.bootstrap.Table.RowSelectionModel = function(config){
21663     Roo.apply(this, config);
21664     this.selections = new Roo.util.MixedCollection(false, function(o){
21665         return o.id;
21666     });
21667
21668     this.last = false;
21669     this.lastActive = false;
21670
21671     this.addEvents({
21672         /**
21673              * @event selectionchange
21674              * Fires when the selection changes
21675              * @param {SelectionModel} this
21676              */
21677             "selectionchange" : true,
21678         /**
21679              * @event afterselectionchange
21680              * Fires after the selection changes (eg. by key press or clicking)
21681              * @param {SelectionModel} this
21682              */
21683             "afterselectionchange" : true,
21684         /**
21685              * @event beforerowselect
21686              * Fires when a row is selected being selected, return false to cancel.
21687              * @param {SelectionModel} this
21688              * @param {Number} rowIndex The selected index
21689              * @param {Boolean} keepExisting False if other selections will be cleared
21690              */
21691             "beforerowselect" : true,
21692         /**
21693              * @event rowselect
21694              * Fires when a row is selected.
21695              * @param {SelectionModel} this
21696              * @param {Number} rowIndex The selected index
21697              * @param {Roo.data.Record} r The record
21698              */
21699             "rowselect" : true,
21700         /**
21701              * @event rowdeselect
21702              * Fires when a row is deselected.
21703              * @param {SelectionModel} this
21704              * @param {Number} rowIndex The selected index
21705              */
21706         "rowdeselect" : true
21707     });
21708     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21709     this.locked = false;
21710 };
21711
21712 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21713     /**
21714      * @cfg {Boolean} singleSelect
21715      * True to allow selection of only one row at a time (defaults to false)
21716      */
21717     singleSelect : false,
21718
21719     // private
21720     initEvents : function(){
21721
21722         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21723             this.grid.on("mousedown", this.handleMouseDown, this);
21724         }else{ // allow click to work like normal
21725             this.grid.on("rowclick", this.handleDragableRowClick, this);
21726         }
21727
21728         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21729             "up" : function(e){
21730                 if(!e.shiftKey){
21731                     this.selectPrevious(e.shiftKey);
21732                 }else if(this.last !== false && this.lastActive !== false){
21733                     var last = this.last;
21734                     this.selectRange(this.last,  this.lastActive-1);
21735                     this.grid.getView().focusRow(this.lastActive);
21736                     if(last !== false){
21737                         this.last = last;
21738                     }
21739                 }else{
21740                     this.selectFirstRow();
21741                 }
21742                 this.fireEvent("afterselectionchange", this);
21743             },
21744             "down" : function(e){
21745                 if(!e.shiftKey){
21746                     this.selectNext(e.shiftKey);
21747                 }else if(this.last !== false && this.lastActive !== false){
21748                     var last = this.last;
21749                     this.selectRange(this.last,  this.lastActive+1);
21750                     this.grid.getView().focusRow(this.lastActive);
21751                     if(last !== false){
21752                         this.last = last;
21753                     }
21754                 }else{
21755                     this.selectFirstRow();
21756                 }
21757                 this.fireEvent("afterselectionchange", this);
21758             },
21759             scope: this
21760         });
21761
21762         var view = this.grid.view;
21763         view.on("refresh", this.onRefresh, this);
21764         view.on("rowupdated", this.onRowUpdated, this);
21765         view.on("rowremoved", this.onRemove, this);
21766     },
21767
21768     // private
21769     onRefresh : function(){
21770         var ds = this.grid.dataSource, i, v = this.grid.view;
21771         var s = this.selections;
21772         s.each(function(r){
21773             if((i = ds.indexOfId(r.id)) != -1){
21774                 v.onRowSelect(i);
21775             }else{
21776                 s.remove(r);
21777             }
21778         });
21779     },
21780
21781     // private
21782     onRemove : function(v, index, r){
21783         this.selections.remove(r);
21784     },
21785
21786     // private
21787     onRowUpdated : function(v, index, r){
21788         if(this.isSelected(r)){
21789             v.onRowSelect(index);
21790         }
21791     },
21792
21793     /**
21794      * Select records.
21795      * @param {Array} records The records to select
21796      * @param {Boolean} keepExisting (optional) True to keep existing selections
21797      */
21798     selectRecords : function(records, keepExisting){
21799         if(!keepExisting){
21800             this.clearSelections();
21801         }
21802         var ds = this.grid.dataSource;
21803         for(var i = 0, len = records.length; i < len; i++){
21804             this.selectRow(ds.indexOf(records[i]), true);
21805         }
21806     },
21807
21808     /**
21809      * Gets the number of selected rows.
21810      * @return {Number}
21811      */
21812     getCount : function(){
21813         return this.selections.length;
21814     },
21815
21816     /**
21817      * Selects the first row in the grid.
21818      */
21819     selectFirstRow : function(){
21820         this.selectRow(0);
21821     },
21822
21823     /**
21824      * Select the last row.
21825      * @param {Boolean} keepExisting (optional) True to keep existing selections
21826      */
21827     selectLastRow : function(keepExisting){
21828         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21829     },
21830
21831     /**
21832      * Selects the row immediately following the last selected row.
21833      * @param {Boolean} keepExisting (optional) True to keep existing selections
21834      */
21835     selectNext : function(keepExisting){
21836         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21837             this.selectRow(this.last+1, keepExisting);
21838             this.grid.getView().focusRow(this.last);
21839         }
21840     },
21841
21842     /**
21843      * Selects the row that precedes the last selected row.
21844      * @param {Boolean} keepExisting (optional) True to keep existing selections
21845      */
21846     selectPrevious : function(keepExisting){
21847         if(this.last){
21848             this.selectRow(this.last-1, keepExisting);
21849             this.grid.getView().focusRow(this.last);
21850         }
21851     },
21852
21853     /**
21854      * Returns the selected records
21855      * @return {Array} Array of selected records
21856      */
21857     getSelections : function(){
21858         return [].concat(this.selections.items);
21859     },
21860
21861     /**
21862      * Returns the first selected record.
21863      * @return {Record}
21864      */
21865     getSelected : function(){
21866         return this.selections.itemAt(0);
21867     },
21868
21869
21870     /**
21871      * Clears all selections.
21872      */
21873     clearSelections : function(fast){
21874         if(this.locked) {
21875             return;
21876         }
21877         if(fast !== true){
21878             var ds = this.grid.dataSource;
21879             var s = this.selections;
21880             s.each(function(r){
21881                 this.deselectRow(ds.indexOfId(r.id));
21882             }, this);
21883             s.clear();
21884         }else{
21885             this.selections.clear();
21886         }
21887         this.last = false;
21888     },
21889
21890
21891     /**
21892      * Selects all rows.
21893      */
21894     selectAll : function(){
21895         if(this.locked) {
21896             return;
21897         }
21898         this.selections.clear();
21899         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21900             this.selectRow(i, true);
21901         }
21902     },
21903
21904     /**
21905      * Returns True if there is a selection.
21906      * @return {Boolean}
21907      */
21908     hasSelection : function(){
21909         return this.selections.length > 0;
21910     },
21911
21912     /**
21913      * Returns True if the specified row is selected.
21914      * @param {Number/Record} record The record or index of the record to check
21915      * @return {Boolean}
21916      */
21917     isSelected : function(index){
21918         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21919         return (r && this.selections.key(r.id) ? true : false);
21920     },
21921
21922     /**
21923      * Returns True if the specified record id is selected.
21924      * @param {String} id The id of record to check
21925      * @return {Boolean}
21926      */
21927     isIdSelected : function(id){
21928         return (this.selections.key(id) ? true : false);
21929     },
21930
21931     // private
21932     handleMouseDown : function(e, t){
21933         var view = this.grid.getView(), rowIndex;
21934         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21935             return;
21936         };
21937         if(e.shiftKey && this.last !== false){
21938             var last = this.last;
21939             this.selectRange(last, rowIndex, e.ctrlKey);
21940             this.last = last; // reset the last
21941             view.focusRow(rowIndex);
21942         }else{
21943             var isSelected = this.isSelected(rowIndex);
21944             if(e.button !== 0 && isSelected){
21945                 view.focusRow(rowIndex);
21946             }else if(e.ctrlKey && isSelected){
21947                 this.deselectRow(rowIndex);
21948             }else if(!isSelected){
21949                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21950                 view.focusRow(rowIndex);
21951             }
21952         }
21953         this.fireEvent("afterselectionchange", this);
21954     },
21955     // private
21956     handleDragableRowClick :  function(grid, rowIndex, e) 
21957     {
21958         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21959             this.selectRow(rowIndex, false);
21960             grid.view.focusRow(rowIndex);
21961              this.fireEvent("afterselectionchange", this);
21962         }
21963     },
21964     
21965     /**
21966      * Selects multiple rows.
21967      * @param {Array} rows Array of the indexes of the row to select
21968      * @param {Boolean} keepExisting (optional) True to keep existing selections
21969      */
21970     selectRows : function(rows, keepExisting){
21971         if(!keepExisting){
21972             this.clearSelections();
21973         }
21974         for(var i = 0, len = rows.length; i < len; i++){
21975             this.selectRow(rows[i], true);
21976         }
21977     },
21978
21979     /**
21980      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21981      * @param {Number} startRow The index of the first row in the range
21982      * @param {Number} endRow The index of the last row in the range
21983      * @param {Boolean} keepExisting (optional) True to retain existing selections
21984      */
21985     selectRange : function(startRow, endRow, keepExisting){
21986         if(this.locked) {
21987             return;
21988         }
21989         if(!keepExisting){
21990             this.clearSelections();
21991         }
21992         if(startRow <= endRow){
21993             for(var i = startRow; i <= endRow; i++){
21994                 this.selectRow(i, true);
21995             }
21996         }else{
21997             for(var i = startRow; i >= endRow; i--){
21998                 this.selectRow(i, true);
21999             }
22000         }
22001     },
22002
22003     /**
22004      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22005      * @param {Number} startRow The index of the first row in the range
22006      * @param {Number} endRow The index of the last row in the range
22007      */
22008     deselectRange : function(startRow, endRow, preventViewNotify){
22009         if(this.locked) {
22010             return;
22011         }
22012         for(var i = startRow; i <= endRow; i++){
22013             this.deselectRow(i, preventViewNotify);
22014         }
22015     },
22016
22017     /**
22018      * Selects a row.
22019      * @param {Number} row The index of the row to select
22020      * @param {Boolean} keepExisting (optional) True to keep existing selections
22021      */
22022     selectRow : function(index, keepExisting, preventViewNotify){
22023         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22024             return;
22025         }
22026         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22027             if(!keepExisting || this.singleSelect){
22028                 this.clearSelections();
22029             }
22030             var r = this.grid.dataSource.getAt(index);
22031             this.selections.add(r);
22032             this.last = this.lastActive = index;
22033             if(!preventViewNotify){
22034                 this.grid.getView().onRowSelect(index);
22035             }
22036             this.fireEvent("rowselect", this, index, r);
22037             this.fireEvent("selectionchange", this);
22038         }
22039     },
22040
22041     /**
22042      * Deselects a row.
22043      * @param {Number} row The index of the row to deselect
22044      */
22045     deselectRow : function(index, preventViewNotify){
22046         if(this.locked) {
22047             return;
22048         }
22049         if(this.last == index){
22050             this.last = false;
22051         }
22052         if(this.lastActive == index){
22053             this.lastActive = false;
22054         }
22055         var r = this.grid.dataSource.getAt(index);
22056         this.selections.remove(r);
22057         if(!preventViewNotify){
22058             this.grid.getView().onRowDeselect(index);
22059         }
22060         this.fireEvent("rowdeselect", this, index);
22061         this.fireEvent("selectionchange", this);
22062     },
22063
22064     // private
22065     restoreLast : function(){
22066         if(this._last){
22067             this.last = this._last;
22068         }
22069     },
22070
22071     // private
22072     acceptsNav : function(row, col, cm){
22073         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22074     },
22075
22076     // private
22077     onEditorKey : function(field, e){
22078         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22079         if(k == e.TAB){
22080             e.stopEvent();
22081             ed.completeEdit();
22082             if(e.shiftKey){
22083                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22084             }else{
22085                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22086             }
22087         }else if(k == e.ENTER && !e.ctrlKey){
22088             e.stopEvent();
22089             ed.completeEdit();
22090             if(e.shiftKey){
22091                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22092             }else{
22093                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22094             }
22095         }else if(k == e.ESC){
22096             ed.cancelEdit();
22097         }
22098         if(newCell){
22099             g.startEditing(newCell[0], newCell[1]);
22100         }
22101     }
22102 });/*
22103  * Based on:
22104  * Ext JS Library 1.1.1
22105  * Copyright(c) 2006-2007, Ext JS, LLC.
22106  *
22107  * Originally Released Under LGPL - original licence link has changed is not relivant.
22108  *
22109  * Fork - LGPL
22110  * <script type="text/javascript">
22111  */
22112  
22113 /**
22114  * @class Roo.bootstrap.PagingToolbar
22115  * @extends Roo.bootstrap.NavSimplebar
22116  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22117  * @constructor
22118  * Create a new PagingToolbar
22119  * @param {Object} config The config object
22120  * @param {Roo.data.Store} store
22121  */
22122 Roo.bootstrap.PagingToolbar = function(config)
22123 {
22124     // old args format still supported... - xtype is prefered..
22125         // created from xtype...
22126     
22127     this.ds = config.dataSource;
22128     
22129     if (config.store && !this.ds) {
22130         this.store= Roo.factory(config.store, Roo.data);
22131         this.ds = this.store;
22132         this.ds.xmodule = this.xmodule || false;
22133     }
22134     
22135     this.toolbarItems = [];
22136     if (config.items) {
22137         this.toolbarItems = config.items;
22138     }
22139     
22140     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22141     
22142     this.cursor = 0;
22143     
22144     if (this.ds) { 
22145         this.bind(this.ds);
22146     }
22147     
22148     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22149     
22150 };
22151
22152 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22153     /**
22154      * @cfg {Roo.data.Store} dataSource
22155      * The underlying data store providing the paged data
22156      */
22157     /**
22158      * @cfg {String/HTMLElement/Element} container
22159      * container The id or element that will contain the toolbar
22160      */
22161     /**
22162      * @cfg {Boolean} displayInfo
22163      * True to display the displayMsg (defaults to false)
22164      */
22165     /**
22166      * @cfg {Number} pageSize
22167      * The number of records to display per page (defaults to 20)
22168      */
22169     pageSize: 20,
22170     /**
22171      * @cfg {String} displayMsg
22172      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22173      */
22174     displayMsg : 'Displaying {0} - {1} of {2}',
22175     /**
22176      * @cfg {String} emptyMsg
22177      * The message to display when no records are found (defaults to "No data to display")
22178      */
22179     emptyMsg : 'No data to display',
22180     /**
22181      * Customizable piece of the default paging text (defaults to "Page")
22182      * @type String
22183      */
22184     beforePageText : "Page",
22185     /**
22186      * Customizable piece of the default paging text (defaults to "of %0")
22187      * @type String
22188      */
22189     afterPageText : "of {0}",
22190     /**
22191      * Customizable piece of the default paging text (defaults to "First Page")
22192      * @type String
22193      */
22194     firstText : "First Page",
22195     /**
22196      * Customizable piece of the default paging text (defaults to "Previous Page")
22197      * @type String
22198      */
22199     prevText : "Previous Page",
22200     /**
22201      * Customizable piece of the default paging text (defaults to "Next Page")
22202      * @type String
22203      */
22204     nextText : "Next Page",
22205     /**
22206      * Customizable piece of the default paging text (defaults to "Last Page")
22207      * @type String
22208      */
22209     lastText : "Last Page",
22210     /**
22211      * Customizable piece of the default paging text (defaults to "Refresh")
22212      * @type String
22213      */
22214     refreshText : "Refresh",
22215
22216     buttons : false,
22217     // private
22218     onRender : function(ct, position) 
22219     {
22220         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22221         this.navgroup.parentId = this.id;
22222         this.navgroup.onRender(this.el, null);
22223         // add the buttons to the navgroup
22224         
22225         if(this.displayInfo){
22226             Roo.log(this.el.select('ul.navbar-nav',true).first());
22227             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22228             this.displayEl = this.el.select('.x-paging-info', true).first();
22229 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22230 //            this.displayEl = navel.el.select('span',true).first();
22231         }
22232         
22233         var _this = this;
22234         
22235         if(this.buttons){
22236             Roo.each(_this.buttons, function(e){ // this might need to use render????
22237                Roo.factory(e).onRender(_this.el, null);
22238             });
22239         }
22240             
22241         Roo.each(_this.toolbarItems, function(e) {
22242             _this.navgroup.addItem(e);
22243         });
22244         
22245         
22246         this.first = this.navgroup.addItem({
22247             tooltip: this.firstText,
22248             cls: "prev",
22249             icon : 'fa fa-backward',
22250             disabled: true,
22251             preventDefault: true,
22252             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22253         });
22254         
22255         this.prev =  this.navgroup.addItem({
22256             tooltip: this.prevText,
22257             cls: "prev",
22258             icon : 'fa fa-step-backward',
22259             disabled: true,
22260             preventDefault: true,
22261             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22262         });
22263     //this.addSeparator();
22264         
22265         
22266         var field = this.navgroup.addItem( {
22267             tagtype : 'span',
22268             cls : 'x-paging-position',
22269             
22270             html : this.beforePageText  +
22271                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22272                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22273          } ); //?? escaped?
22274         
22275         this.field = field.el.select('input', true).first();
22276         this.field.on("keydown", this.onPagingKeydown, this);
22277         this.field.on("focus", function(){this.dom.select();});
22278     
22279     
22280         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22281         //this.field.setHeight(18);
22282         //this.addSeparator();
22283         this.next = this.navgroup.addItem({
22284             tooltip: this.nextText,
22285             cls: "next",
22286             html : ' <i class="fa fa-step-forward">',
22287             disabled: true,
22288             preventDefault: true,
22289             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22290         });
22291         this.last = this.navgroup.addItem({
22292             tooltip: this.lastText,
22293             icon : 'fa fa-forward',
22294             cls: "next",
22295             disabled: true,
22296             preventDefault: true,
22297             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22298         });
22299     //this.addSeparator();
22300         this.loading = this.navgroup.addItem({
22301             tooltip: this.refreshText,
22302             icon: 'fa fa-refresh',
22303             preventDefault: true,
22304             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22305         });
22306         
22307     },
22308
22309     // private
22310     updateInfo : function(){
22311         if(this.displayEl){
22312             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22313             var msg = count == 0 ?
22314                 this.emptyMsg :
22315                 String.format(
22316                     this.displayMsg,
22317                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22318                 );
22319             this.displayEl.update(msg);
22320         }
22321     },
22322
22323     // private
22324     onLoad : function(ds, r, o){
22325        this.cursor = o.params ? o.params.start : 0;
22326        var d = this.getPageData(),
22327             ap = d.activePage,
22328             ps = d.pages;
22329         
22330        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22331        this.field.dom.value = ap;
22332        this.first.setDisabled(ap == 1);
22333        this.prev.setDisabled(ap == 1);
22334        this.next.setDisabled(ap == ps);
22335        this.last.setDisabled(ap == ps);
22336        this.loading.enable();
22337        this.updateInfo();
22338     },
22339
22340     // private
22341     getPageData : function(){
22342         var total = this.ds.getTotalCount();
22343         return {
22344             total : total,
22345             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22346             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22347         };
22348     },
22349
22350     // private
22351     onLoadError : function(){
22352         this.loading.enable();
22353     },
22354
22355     // private
22356     onPagingKeydown : function(e){
22357         var k = e.getKey();
22358         var d = this.getPageData();
22359         if(k == e.RETURN){
22360             var v = this.field.dom.value, pageNum;
22361             if(!v || isNaN(pageNum = parseInt(v, 10))){
22362                 this.field.dom.value = d.activePage;
22363                 return;
22364             }
22365             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22366             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22367             e.stopEvent();
22368         }
22369         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))
22370         {
22371           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22372           this.field.dom.value = pageNum;
22373           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22374           e.stopEvent();
22375         }
22376         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22377         {
22378           var v = this.field.dom.value, pageNum; 
22379           var increment = (e.shiftKey) ? 10 : 1;
22380           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22381                 increment *= -1;
22382           }
22383           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22384             this.field.dom.value = d.activePage;
22385             return;
22386           }
22387           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22388           {
22389             this.field.dom.value = parseInt(v, 10) + increment;
22390             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22391             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22392           }
22393           e.stopEvent();
22394         }
22395     },
22396
22397     // private
22398     beforeLoad : function(){
22399         if(this.loading){
22400             this.loading.disable();
22401         }
22402     },
22403
22404     // private
22405     onClick : function(which){
22406         
22407         var ds = this.ds;
22408         if (!ds) {
22409             return;
22410         }
22411         
22412         switch(which){
22413             case "first":
22414                 ds.load({params:{start: 0, limit: this.pageSize}});
22415             break;
22416             case "prev":
22417                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22418             break;
22419             case "next":
22420                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22421             break;
22422             case "last":
22423                 var total = ds.getTotalCount();
22424                 var extra = total % this.pageSize;
22425                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22426                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22427             break;
22428             case "refresh":
22429                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22430             break;
22431         }
22432     },
22433
22434     /**
22435      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22436      * @param {Roo.data.Store} store The data store to unbind
22437      */
22438     unbind : function(ds){
22439         ds.un("beforeload", this.beforeLoad, this);
22440         ds.un("load", this.onLoad, this);
22441         ds.un("loadexception", this.onLoadError, this);
22442         ds.un("remove", this.updateInfo, this);
22443         ds.un("add", this.updateInfo, this);
22444         this.ds = undefined;
22445     },
22446
22447     /**
22448      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22449      * @param {Roo.data.Store} store The data store to bind
22450      */
22451     bind : function(ds){
22452         ds.on("beforeload", this.beforeLoad, this);
22453         ds.on("load", this.onLoad, this);
22454         ds.on("loadexception", this.onLoadError, this);
22455         ds.on("remove", this.updateInfo, this);
22456         ds.on("add", this.updateInfo, this);
22457         this.ds = ds;
22458     }
22459 });/*
22460  * - LGPL
22461  *
22462  * element
22463  * 
22464  */
22465
22466 /**
22467  * @class Roo.bootstrap.MessageBar
22468  * @extends Roo.bootstrap.Component
22469  * Bootstrap MessageBar class
22470  * @cfg {String} html contents of the MessageBar
22471  * @cfg {String} weight (info | success | warning | danger) default info
22472  * @cfg {String} beforeClass insert the bar before the given class
22473  * @cfg {Boolean} closable (true | false) default false
22474  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22475  * 
22476  * @constructor
22477  * Create a new Element
22478  * @param {Object} config The config object
22479  */
22480
22481 Roo.bootstrap.MessageBar = function(config){
22482     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22483 };
22484
22485 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22486     
22487     html: '',
22488     weight: 'info',
22489     closable: false,
22490     fixed: false,
22491     beforeClass: 'bootstrap-sticky-wrap',
22492     
22493     getAutoCreate : function(){
22494         
22495         var cfg = {
22496             tag: 'div',
22497             cls: 'alert alert-dismissable alert-' + this.weight,
22498             cn: [
22499                 {
22500                     tag: 'span',
22501                     cls: 'message',
22502                     html: this.html || ''
22503                 }
22504             ]
22505         };
22506         
22507         if(this.fixed){
22508             cfg.cls += ' alert-messages-fixed';
22509         }
22510         
22511         if(this.closable){
22512             cfg.cn.push({
22513                 tag: 'button',
22514                 cls: 'close',
22515                 html: 'x'
22516             });
22517         }
22518         
22519         return cfg;
22520     },
22521     
22522     onRender : function(ct, position)
22523     {
22524         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22525         
22526         if(!this.el){
22527             var cfg = Roo.apply({},  this.getAutoCreate());
22528             cfg.id = Roo.id();
22529             
22530             if (this.cls) {
22531                 cfg.cls += ' ' + this.cls;
22532             }
22533             if (this.style) {
22534                 cfg.style = this.style;
22535             }
22536             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22537             
22538             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22539         }
22540         
22541         this.el.select('>button.close').on('click', this.hide, this);
22542         
22543     },
22544     
22545     show : function()
22546     {
22547         if (!this.rendered) {
22548             this.render();
22549         }
22550         
22551         this.el.show();
22552         
22553         this.fireEvent('show', this);
22554         
22555     },
22556     
22557     hide : function()
22558     {
22559         if (!this.rendered) {
22560             this.render();
22561         }
22562         
22563         this.el.hide();
22564         
22565         this.fireEvent('hide', this);
22566     },
22567     
22568     update : function()
22569     {
22570 //        var e = this.el.dom.firstChild;
22571 //        
22572 //        if(this.closable){
22573 //            e = e.nextSibling;
22574 //        }
22575 //        
22576 //        e.data = this.html || '';
22577
22578         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22579     }
22580    
22581 });
22582
22583  
22584
22585      /*
22586  * - LGPL
22587  *
22588  * Graph
22589  * 
22590  */
22591
22592
22593 /**
22594  * @class Roo.bootstrap.Graph
22595  * @extends Roo.bootstrap.Component
22596  * Bootstrap Graph class
22597 > Prameters
22598  -sm {number} sm 4
22599  -md {number} md 5
22600  @cfg {String} graphtype  bar | vbar | pie
22601  @cfg {number} g_x coodinator | centre x (pie)
22602  @cfg {number} g_y coodinator | centre y (pie)
22603  @cfg {number} g_r radius (pie)
22604  @cfg {number} g_height height of the chart (respected by all elements in the set)
22605  @cfg {number} g_width width of the chart (respected by all elements in the set)
22606  @cfg {Object} title The title of the chart
22607     
22608  -{Array}  values
22609  -opts (object) options for the chart 
22610      o {
22611      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22612      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22613      o vgutter (number)
22614      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.
22615      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22616      o to
22617      o stretch (boolean)
22618      o }
22619  -opts (object) options for the pie
22620      o{
22621      o cut
22622      o startAngle (number)
22623      o endAngle (number)
22624      } 
22625  *
22626  * @constructor
22627  * Create a new Input
22628  * @param {Object} config The config object
22629  */
22630
22631 Roo.bootstrap.Graph = function(config){
22632     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22633     
22634     this.addEvents({
22635         // img events
22636         /**
22637          * @event click
22638          * The img click event for the img.
22639          * @param {Roo.EventObject} e
22640          */
22641         "click" : true
22642     });
22643 };
22644
22645 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22646     
22647     sm: 4,
22648     md: 5,
22649     graphtype: 'bar',
22650     g_height: 250,
22651     g_width: 400,
22652     g_x: 50,
22653     g_y: 50,
22654     g_r: 30,
22655     opts:{
22656         //g_colors: this.colors,
22657         g_type: 'soft',
22658         g_gutter: '20%'
22659
22660     },
22661     title : false,
22662
22663     getAutoCreate : function(){
22664         
22665         var cfg = {
22666             tag: 'div',
22667             html : null
22668         };
22669         
22670         
22671         return  cfg;
22672     },
22673
22674     onRender : function(ct,position){
22675         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22676         this.raphael = Raphael(this.el.dom);
22677         
22678                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22679                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22680                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22681                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22682                 /*
22683                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22684                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22685                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22686                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22687                 
22688                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22689                 r.barchart(330, 10, 300, 220, data1);
22690                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22691                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22692                 */
22693                 
22694                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22695                 // r.barchart(30, 30, 560, 250,  xdata, {
22696                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22697                 //     axis : "0 0 1 1",
22698                 //     axisxlabels :  xdata
22699                 //     //yvalues : cols,
22700                    
22701                 // });
22702 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22703 //        
22704 //        this.load(null,xdata,{
22705 //                axis : "0 0 1 1",
22706 //                axisxlabels :  xdata
22707 //                });
22708
22709     },
22710
22711     load : function(graphtype,xdata,opts){
22712         this.raphael.clear();
22713         if(!graphtype) {
22714             graphtype = this.graphtype;
22715         }
22716         if(!opts){
22717             opts = this.opts;
22718         }
22719         var r = this.raphael,
22720             fin = function () {
22721                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22722             },
22723             fout = function () {
22724                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22725             },
22726             pfin = function() {
22727                 this.sector.stop();
22728                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22729
22730                 if (this.label) {
22731                     this.label[0].stop();
22732                     this.label[0].attr({ r: 7.5 });
22733                     this.label[1].attr({ "font-weight": 800 });
22734                 }
22735             },
22736             pfout = function() {
22737                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22738
22739                 if (this.label) {
22740                     this.label[0].animate({ r: 5 }, 500, "bounce");
22741                     this.label[1].attr({ "font-weight": 400 });
22742                 }
22743             };
22744
22745         switch(graphtype){
22746             case 'bar':
22747                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22748                 break;
22749             case 'hbar':
22750                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22751                 break;
22752             case 'pie':
22753 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22754 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22755 //            
22756                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22757                 
22758                 break;
22759
22760         }
22761         
22762         if(this.title){
22763             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22764         }
22765         
22766     },
22767     
22768     setTitle: function(o)
22769     {
22770         this.title = o;
22771     },
22772     
22773     initEvents: function() {
22774         
22775         if(!this.href){
22776             this.el.on('click', this.onClick, this);
22777         }
22778     },
22779     
22780     onClick : function(e)
22781     {
22782         Roo.log('img onclick');
22783         this.fireEvent('click', this, e);
22784     }
22785    
22786 });
22787
22788  
22789 /*
22790  * - LGPL
22791  *
22792  * numberBox
22793  * 
22794  */
22795 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22796
22797 /**
22798  * @class Roo.bootstrap.dash.NumberBox
22799  * @extends Roo.bootstrap.Component
22800  * Bootstrap NumberBox class
22801  * @cfg {String} headline Box headline
22802  * @cfg {String} content Box content
22803  * @cfg {String} icon Box icon
22804  * @cfg {String} footer Footer text
22805  * @cfg {String} fhref Footer href
22806  * 
22807  * @constructor
22808  * Create a new NumberBox
22809  * @param {Object} config The config object
22810  */
22811
22812
22813 Roo.bootstrap.dash.NumberBox = function(config){
22814     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22815     
22816 };
22817
22818 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22819     
22820     headline : '',
22821     content : '',
22822     icon : '',
22823     footer : '',
22824     fhref : '',
22825     ficon : '',
22826     
22827     getAutoCreate : function(){
22828         
22829         var cfg = {
22830             tag : 'div',
22831             cls : 'small-box ',
22832             cn : [
22833                 {
22834                     tag : 'div',
22835                     cls : 'inner',
22836                     cn :[
22837                         {
22838                             tag : 'h3',
22839                             cls : 'roo-headline',
22840                             html : this.headline
22841                         },
22842                         {
22843                             tag : 'p',
22844                             cls : 'roo-content',
22845                             html : this.content
22846                         }
22847                     ]
22848                 }
22849             ]
22850         };
22851         
22852         if(this.icon){
22853             cfg.cn.push({
22854                 tag : 'div',
22855                 cls : 'icon',
22856                 cn :[
22857                     {
22858                         tag : 'i',
22859                         cls : 'ion ' + this.icon
22860                     }
22861                 ]
22862             });
22863         }
22864         
22865         if(this.footer){
22866             var footer = {
22867                 tag : 'a',
22868                 cls : 'small-box-footer',
22869                 href : this.fhref || '#',
22870                 html : this.footer
22871             };
22872             
22873             cfg.cn.push(footer);
22874             
22875         }
22876         
22877         return  cfg;
22878     },
22879
22880     onRender : function(ct,position){
22881         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22882
22883
22884        
22885                 
22886     },
22887
22888     setHeadline: function (value)
22889     {
22890         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22891     },
22892     
22893     setFooter: function (value, href)
22894     {
22895         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22896         
22897         if(href){
22898             this.el.select('a.small-box-footer',true).first().attr('href', href);
22899         }
22900         
22901     },
22902
22903     setContent: function (value)
22904     {
22905         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22906     },
22907
22908     initEvents: function() 
22909     {   
22910         
22911     }
22912     
22913 });
22914
22915  
22916 /*
22917  * - LGPL
22918  *
22919  * TabBox
22920  * 
22921  */
22922 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22923
22924 /**
22925  * @class Roo.bootstrap.dash.TabBox
22926  * @extends Roo.bootstrap.Component
22927  * Bootstrap TabBox class
22928  * @cfg {String} title Title of the TabBox
22929  * @cfg {String} icon Icon of the TabBox
22930  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22931  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22932  * 
22933  * @constructor
22934  * Create a new TabBox
22935  * @param {Object} config The config object
22936  */
22937
22938
22939 Roo.bootstrap.dash.TabBox = function(config){
22940     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22941     this.addEvents({
22942         // raw events
22943         /**
22944          * @event addpane
22945          * When a pane is added
22946          * @param {Roo.bootstrap.dash.TabPane} pane
22947          */
22948         "addpane" : true,
22949         /**
22950          * @event activatepane
22951          * When a pane is activated
22952          * @param {Roo.bootstrap.dash.TabPane} pane
22953          */
22954         "activatepane" : true
22955         
22956          
22957     });
22958     
22959     this.panes = [];
22960 };
22961
22962 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22963
22964     title : '',
22965     icon : false,
22966     showtabs : true,
22967     tabScrollable : false,
22968     
22969     getChildContainer : function()
22970     {
22971         return this.el.select('.tab-content', true).first();
22972     },
22973     
22974     getAutoCreate : function(){
22975         
22976         var header = {
22977             tag: 'li',
22978             cls: 'pull-left header',
22979             html: this.title,
22980             cn : []
22981         };
22982         
22983         if(this.icon){
22984             header.cn.push({
22985                 tag: 'i',
22986                 cls: 'fa ' + this.icon
22987             });
22988         }
22989         
22990         var h = {
22991             tag: 'ul',
22992             cls: 'nav nav-tabs pull-right',
22993             cn: [
22994                 header
22995             ]
22996         };
22997         
22998         if(this.tabScrollable){
22999             h = {
23000                 tag: 'div',
23001                 cls: 'tab-header',
23002                 cn: [
23003                     {
23004                         tag: 'ul',
23005                         cls: 'nav nav-tabs pull-right',
23006                         cn: [
23007                             header
23008                         ]
23009                     }
23010                 ]
23011             };
23012         }
23013         
23014         var cfg = {
23015             tag: 'div',
23016             cls: 'nav-tabs-custom',
23017             cn: [
23018                 h,
23019                 {
23020                     tag: 'div',
23021                     cls: 'tab-content no-padding',
23022                     cn: []
23023                 }
23024             ]
23025         };
23026
23027         return  cfg;
23028     },
23029     initEvents : function()
23030     {
23031         //Roo.log('add add pane handler');
23032         this.on('addpane', this.onAddPane, this);
23033     },
23034      /**
23035      * Updates the box title
23036      * @param {String} html to set the title to.
23037      */
23038     setTitle : function(value)
23039     {
23040         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23041     },
23042     onAddPane : function(pane)
23043     {
23044         this.panes.push(pane);
23045         //Roo.log('addpane');
23046         //Roo.log(pane);
23047         // tabs are rendere left to right..
23048         if(!this.showtabs){
23049             return;
23050         }
23051         
23052         var ctr = this.el.select('.nav-tabs', true).first();
23053          
23054          
23055         var existing = ctr.select('.nav-tab',true);
23056         var qty = existing.getCount();;
23057         
23058         
23059         var tab = ctr.createChild({
23060             tag : 'li',
23061             cls : 'nav-tab' + (qty ? '' : ' active'),
23062             cn : [
23063                 {
23064                     tag : 'a',
23065                     href:'#',
23066                     html : pane.title
23067                 }
23068             ]
23069         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23070         pane.tab = tab;
23071         
23072         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23073         if (!qty) {
23074             pane.el.addClass('active');
23075         }
23076         
23077                 
23078     },
23079     onTabClick : function(ev,un,ob,pane)
23080     {
23081         //Roo.log('tab - prev default');
23082         ev.preventDefault();
23083         
23084         
23085         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23086         pane.tab.addClass('active');
23087         //Roo.log(pane.title);
23088         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23089         // technically we should have a deactivate event.. but maybe add later.
23090         // and it should not de-activate the selected tab...
23091         this.fireEvent('activatepane', pane);
23092         pane.el.addClass('active');
23093         pane.fireEvent('activate');
23094         
23095         
23096     },
23097     
23098     getActivePane : function()
23099     {
23100         var r = false;
23101         Roo.each(this.panes, function(p) {
23102             if(p.el.hasClass('active')){
23103                 r = p;
23104                 return false;
23105             }
23106             
23107             return;
23108         });
23109         
23110         return r;
23111     }
23112     
23113     
23114 });
23115
23116  
23117 /*
23118  * - LGPL
23119  *
23120  * Tab pane
23121  * 
23122  */
23123 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23124 /**
23125  * @class Roo.bootstrap.TabPane
23126  * @extends Roo.bootstrap.Component
23127  * Bootstrap TabPane class
23128  * @cfg {Boolean} active (false | true) Default false
23129  * @cfg {String} title title of panel
23130
23131  * 
23132  * @constructor
23133  * Create a new TabPane
23134  * @param {Object} config The config object
23135  */
23136
23137 Roo.bootstrap.dash.TabPane = function(config){
23138     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23139     
23140     this.addEvents({
23141         // raw events
23142         /**
23143          * @event activate
23144          * When a pane is activated
23145          * @param {Roo.bootstrap.dash.TabPane} pane
23146          */
23147         "activate" : true
23148          
23149     });
23150 };
23151
23152 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23153     
23154     active : false,
23155     title : '',
23156     
23157     // the tabBox that this is attached to.
23158     tab : false,
23159      
23160     getAutoCreate : function() 
23161     {
23162         var cfg = {
23163             tag: 'div',
23164             cls: 'tab-pane'
23165         };
23166         
23167         if(this.active){
23168             cfg.cls += ' active';
23169         }
23170         
23171         return cfg;
23172     },
23173     initEvents  : function()
23174     {
23175         //Roo.log('trigger add pane handler');
23176         this.parent().fireEvent('addpane', this)
23177     },
23178     
23179      /**
23180      * Updates the tab title 
23181      * @param {String} html to set the title to.
23182      */
23183     setTitle: function(str)
23184     {
23185         if (!this.tab) {
23186             return;
23187         }
23188         this.title = str;
23189         this.tab.select('a', true).first().dom.innerHTML = str;
23190         
23191     }
23192     
23193     
23194     
23195 });
23196
23197  
23198
23199
23200  /*
23201  * - LGPL
23202  *
23203  * menu
23204  * 
23205  */
23206 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23207
23208 /**
23209  * @class Roo.bootstrap.menu.Menu
23210  * @extends Roo.bootstrap.Component
23211  * Bootstrap Menu class - container for Menu
23212  * @cfg {String} html Text of the menu
23213  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23214  * @cfg {String} icon Font awesome icon
23215  * @cfg {String} pos Menu align to (top | bottom) default bottom
23216  * 
23217  * 
23218  * @constructor
23219  * Create a new Menu
23220  * @param {Object} config The config object
23221  */
23222
23223
23224 Roo.bootstrap.menu.Menu = function(config){
23225     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23226     
23227     this.addEvents({
23228         /**
23229          * @event beforeshow
23230          * Fires before this menu is displayed
23231          * @param {Roo.bootstrap.menu.Menu} this
23232          */
23233         beforeshow : true,
23234         /**
23235          * @event beforehide
23236          * Fires before this menu is hidden
23237          * @param {Roo.bootstrap.menu.Menu} this
23238          */
23239         beforehide : true,
23240         /**
23241          * @event show
23242          * Fires after this menu is displayed
23243          * @param {Roo.bootstrap.menu.Menu} this
23244          */
23245         show : true,
23246         /**
23247          * @event hide
23248          * Fires after this menu is hidden
23249          * @param {Roo.bootstrap.menu.Menu} this
23250          */
23251         hide : true,
23252         /**
23253          * @event click
23254          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23255          * @param {Roo.bootstrap.menu.Menu} this
23256          * @param {Roo.EventObject} e
23257          */
23258         click : true
23259     });
23260     
23261 };
23262
23263 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23264     
23265     submenu : false,
23266     html : '',
23267     weight : 'default',
23268     icon : false,
23269     pos : 'bottom',
23270     
23271     
23272     getChildContainer : function() {
23273         if(this.isSubMenu){
23274             return this.el;
23275         }
23276         
23277         return this.el.select('ul.dropdown-menu', true).first();  
23278     },
23279     
23280     getAutoCreate : function()
23281     {
23282         var text = [
23283             {
23284                 tag : 'span',
23285                 cls : 'roo-menu-text',
23286                 html : this.html
23287             }
23288         ];
23289         
23290         if(this.icon){
23291             text.unshift({
23292                 tag : 'i',
23293                 cls : 'fa ' + this.icon
23294             })
23295         }
23296         
23297         
23298         var cfg = {
23299             tag : 'div',
23300             cls : 'btn-group',
23301             cn : [
23302                 {
23303                     tag : 'button',
23304                     cls : 'dropdown-button btn btn-' + this.weight,
23305                     cn : text
23306                 },
23307                 {
23308                     tag : 'button',
23309                     cls : 'dropdown-toggle btn btn-' + this.weight,
23310                     cn : [
23311                         {
23312                             tag : 'span',
23313                             cls : 'caret'
23314                         }
23315                     ]
23316                 },
23317                 {
23318                     tag : 'ul',
23319                     cls : 'dropdown-menu'
23320                 }
23321             ]
23322             
23323         };
23324         
23325         if(this.pos == 'top'){
23326             cfg.cls += ' dropup';
23327         }
23328         
23329         if(this.isSubMenu){
23330             cfg = {
23331                 tag : 'ul',
23332                 cls : 'dropdown-menu'
23333             }
23334         }
23335         
23336         return cfg;
23337     },
23338     
23339     onRender : function(ct, position)
23340     {
23341         this.isSubMenu = ct.hasClass('dropdown-submenu');
23342         
23343         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23344     },
23345     
23346     initEvents : function() 
23347     {
23348         if(this.isSubMenu){
23349             return;
23350         }
23351         
23352         this.hidden = true;
23353         
23354         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23355         this.triggerEl.on('click', this.onTriggerPress, this);
23356         
23357         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23358         this.buttonEl.on('click', this.onClick, this);
23359         
23360     },
23361     
23362     list : function()
23363     {
23364         if(this.isSubMenu){
23365             return this.el;
23366         }
23367         
23368         return this.el.select('ul.dropdown-menu', true).first();
23369     },
23370     
23371     onClick : function(e)
23372     {
23373         this.fireEvent("click", this, e);
23374     },
23375     
23376     onTriggerPress  : function(e)
23377     {   
23378         if (this.isVisible()) {
23379             this.hide();
23380         } else {
23381             this.show();
23382         }
23383     },
23384     
23385     isVisible : function(){
23386         return !this.hidden;
23387     },
23388     
23389     show : function()
23390     {
23391         this.fireEvent("beforeshow", this);
23392         
23393         this.hidden = false;
23394         this.el.addClass('open');
23395         
23396         Roo.get(document).on("mouseup", this.onMouseUp, this);
23397         
23398         this.fireEvent("show", this);
23399         
23400         
23401     },
23402     
23403     hide : function()
23404     {
23405         this.fireEvent("beforehide", this);
23406         
23407         this.hidden = true;
23408         this.el.removeClass('open');
23409         
23410         Roo.get(document).un("mouseup", this.onMouseUp);
23411         
23412         this.fireEvent("hide", this);
23413     },
23414     
23415     onMouseUp : function()
23416     {
23417         this.hide();
23418     }
23419     
23420 });
23421
23422  
23423  /*
23424  * - LGPL
23425  *
23426  * menu item
23427  * 
23428  */
23429 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23430
23431 /**
23432  * @class Roo.bootstrap.menu.Item
23433  * @extends Roo.bootstrap.Component
23434  * Bootstrap MenuItem class
23435  * @cfg {Boolean} submenu (true | false) default false
23436  * @cfg {String} html text of the item
23437  * @cfg {String} href the link
23438  * @cfg {Boolean} disable (true | false) default false
23439  * @cfg {Boolean} preventDefault (true | false) default true
23440  * @cfg {String} icon Font awesome icon
23441  * @cfg {String} pos Submenu align to (left | right) default right 
23442  * 
23443  * 
23444  * @constructor
23445  * Create a new Item
23446  * @param {Object} config The config object
23447  */
23448
23449
23450 Roo.bootstrap.menu.Item = function(config){
23451     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23452     this.addEvents({
23453         /**
23454          * @event mouseover
23455          * Fires when the mouse is hovering over this menu
23456          * @param {Roo.bootstrap.menu.Item} this
23457          * @param {Roo.EventObject} e
23458          */
23459         mouseover : true,
23460         /**
23461          * @event mouseout
23462          * Fires when the mouse exits this menu
23463          * @param {Roo.bootstrap.menu.Item} this
23464          * @param {Roo.EventObject} e
23465          */
23466         mouseout : true,
23467         // raw events
23468         /**
23469          * @event click
23470          * The raw click event for the entire grid.
23471          * @param {Roo.EventObject} e
23472          */
23473         click : true
23474     });
23475 };
23476
23477 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23478     
23479     submenu : false,
23480     href : '',
23481     html : '',
23482     preventDefault: true,
23483     disable : false,
23484     icon : false,
23485     pos : 'right',
23486     
23487     getAutoCreate : function()
23488     {
23489         var text = [
23490             {
23491                 tag : 'span',
23492                 cls : 'roo-menu-item-text',
23493                 html : this.html
23494             }
23495         ];
23496         
23497         if(this.icon){
23498             text.unshift({
23499                 tag : 'i',
23500                 cls : 'fa ' + this.icon
23501             })
23502         }
23503         
23504         var cfg = {
23505             tag : 'li',
23506             cn : [
23507                 {
23508                     tag : 'a',
23509                     href : this.href || '#',
23510                     cn : text
23511                 }
23512             ]
23513         };
23514         
23515         if(this.disable){
23516             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23517         }
23518         
23519         if(this.submenu){
23520             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23521             
23522             if(this.pos == 'left'){
23523                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23524             }
23525         }
23526         
23527         return cfg;
23528     },
23529     
23530     initEvents : function() 
23531     {
23532         this.el.on('mouseover', this.onMouseOver, this);
23533         this.el.on('mouseout', this.onMouseOut, this);
23534         
23535         this.el.select('a', true).first().on('click', this.onClick, this);
23536         
23537     },
23538     
23539     onClick : function(e)
23540     {
23541         if(this.preventDefault){
23542             e.preventDefault();
23543         }
23544         
23545         this.fireEvent("click", this, e);
23546     },
23547     
23548     onMouseOver : function(e)
23549     {
23550         if(this.submenu && this.pos == 'left'){
23551             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23552         }
23553         
23554         this.fireEvent("mouseover", this, e);
23555     },
23556     
23557     onMouseOut : function(e)
23558     {
23559         this.fireEvent("mouseout", this, e);
23560     }
23561 });
23562
23563  
23564
23565  /*
23566  * - LGPL
23567  *
23568  * menu separator
23569  * 
23570  */
23571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23572
23573 /**
23574  * @class Roo.bootstrap.menu.Separator
23575  * @extends Roo.bootstrap.Component
23576  * Bootstrap Separator class
23577  * 
23578  * @constructor
23579  * Create a new Separator
23580  * @param {Object} config The config object
23581  */
23582
23583
23584 Roo.bootstrap.menu.Separator = function(config){
23585     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23586 };
23587
23588 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23589     
23590     getAutoCreate : function(){
23591         var cfg = {
23592             tag : 'li',
23593             cls: 'divider'
23594         };
23595         
23596         return cfg;
23597     }
23598    
23599 });
23600
23601  
23602
23603  /*
23604  * - LGPL
23605  *
23606  * Tooltip
23607  * 
23608  */
23609
23610 /**
23611  * @class Roo.bootstrap.Tooltip
23612  * Bootstrap Tooltip class
23613  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23614  * to determine which dom element triggers the tooltip.
23615  * 
23616  * It needs to add support for additional attributes like tooltip-position
23617  * 
23618  * @constructor
23619  * Create a new Toolti
23620  * @param {Object} config The config object
23621  */
23622
23623 Roo.bootstrap.Tooltip = function(config){
23624     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23625 };
23626
23627 Roo.apply(Roo.bootstrap.Tooltip, {
23628     /**
23629      * @function init initialize tooltip monitoring.
23630      * @static
23631      */
23632     currentEl : false,
23633     currentTip : false,
23634     currentRegion : false,
23635     
23636     //  init : delay?
23637     
23638     init : function()
23639     {
23640         Roo.get(document).on('mouseover', this.enter ,this);
23641         Roo.get(document).on('mouseout', this.leave, this);
23642          
23643         
23644         this.currentTip = new Roo.bootstrap.Tooltip();
23645     },
23646     
23647     enter : function(ev)
23648     {
23649         var dom = ev.getTarget();
23650         
23651         //Roo.log(['enter',dom]);
23652         var el = Roo.fly(dom);
23653         if (this.currentEl) {
23654             //Roo.log(dom);
23655             //Roo.log(this.currentEl);
23656             //Roo.log(this.currentEl.contains(dom));
23657             if (this.currentEl == el) {
23658                 return;
23659             }
23660             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23661                 return;
23662             }
23663
23664         }
23665         
23666         if (this.currentTip.el) {
23667             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23668         }    
23669         //Roo.log(ev);
23670         var bindEl = el;
23671         
23672         // you can not look for children, as if el is the body.. then everythign is the child..
23673         if (!el.attr('tooltip')) { //
23674             if (!el.select("[tooltip]").elements.length) {
23675                 return;
23676             }
23677             // is the mouse over this child...?
23678             bindEl = el.select("[tooltip]").first();
23679             var xy = ev.getXY();
23680             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23681                 //Roo.log("not in region.");
23682                 return;
23683             }
23684             //Roo.log("child element over..");
23685             
23686         }
23687         this.currentEl = bindEl;
23688         this.currentTip.bind(bindEl);
23689         this.currentRegion = Roo.lib.Region.getRegion(dom);
23690         this.currentTip.enter();
23691         
23692     },
23693     leave : function(ev)
23694     {
23695         var dom = ev.getTarget();
23696         //Roo.log(['leave',dom]);
23697         if (!this.currentEl) {
23698             return;
23699         }
23700         
23701         
23702         if (dom != this.currentEl.dom) {
23703             return;
23704         }
23705         var xy = ev.getXY();
23706         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23707             return;
23708         }
23709         // only activate leave if mouse cursor is outside... bounding box..
23710         
23711         
23712         
23713         
23714         if (this.currentTip) {
23715             this.currentTip.leave();
23716         }
23717         //Roo.log('clear currentEl');
23718         this.currentEl = false;
23719         
23720         
23721     },
23722     alignment : {
23723         'left' : ['r-l', [-2,0], 'right'],
23724         'right' : ['l-r', [2,0], 'left'],
23725         'bottom' : ['t-b', [0,2], 'top'],
23726         'top' : [ 'b-t', [0,-2], 'bottom']
23727     }
23728     
23729 });
23730
23731
23732 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23733     
23734     
23735     bindEl : false,
23736     
23737     delay : null, // can be { show : 300 , hide: 500}
23738     
23739     timeout : null,
23740     
23741     hoverState : null, //???
23742     
23743     placement : 'bottom', 
23744     
23745     getAutoCreate : function(){
23746     
23747         var cfg = {
23748            cls : 'tooltip',
23749            role : 'tooltip',
23750            cn : [
23751                 {
23752                     cls : 'tooltip-arrow'
23753                 },
23754                 {
23755                     cls : 'tooltip-inner'
23756                 }
23757            ]
23758         };
23759         
23760         return cfg;
23761     },
23762     bind : function(el)
23763     {
23764         this.bindEl = el;
23765     },
23766       
23767     
23768     enter : function () {
23769        
23770         if (this.timeout != null) {
23771             clearTimeout(this.timeout);
23772         }
23773         
23774         this.hoverState = 'in';
23775          //Roo.log("enter - show");
23776         if (!this.delay || !this.delay.show) {
23777             this.show();
23778             return;
23779         }
23780         var _t = this;
23781         this.timeout = setTimeout(function () {
23782             if (_t.hoverState == 'in') {
23783                 _t.show();
23784             }
23785         }, this.delay.show);
23786     },
23787     leave : function()
23788     {
23789         clearTimeout(this.timeout);
23790     
23791         this.hoverState = 'out';
23792          if (!this.delay || !this.delay.hide) {
23793             this.hide();
23794             return;
23795         }
23796        
23797         var _t = this;
23798         this.timeout = setTimeout(function () {
23799             //Roo.log("leave - timeout");
23800             
23801             if (_t.hoverState == 'out') {
23802                 _t.hide();
23803                 Roo.bootstrap.Tooltip.currentEl = false;
23804             }
23805         }, delay);
23806     },
23807     
23808     show : function ()
23809     {
23810         if (!this.el) {
23811             this.render(document.body);
23812         }
23813         // set content.
23814         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23815         
23816         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23817         
23818         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23819         
23820         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23821         
23822         var placement = typeof this.placement == 'function' ?
23823             this.placement.call(this, this.el, on_el) :
23824             this.placement;
23825             
23826         var autoToken = /\s?auto?\s?/i;
23827         var autoPlace = autoToken.test(placement);
23828         if (autoPlace) {
23829             placement = placement.replace(autoToken, '') || 'top';
23830         }
23831         
23832         //this.el.detach()
23833         //this.el.setXY([0,0]);
23834         this.el.show();
23835         //this.el.dom.style.display='block';
23836         
23837         //this.el.appendTo(on_el);
23838         
23839         var p = this.getPosition();
23840         var box = this.el.getBox();
23841         
23842         if (autoPlace) {
23843             // fixme..
23844         }
23845         
23846         var align = Roo.bootstrap.Tooltip.alignment[placement];
23847         
23848         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23849         
23850         if(placement == 'top' || placement == 'bottom'){
23851             if(xy[0] < 0){
23852                 placement = 'right';
23853             }
23854             
23855             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23856                 placement = 'left';
23857             }
23858         }
23859         
23860         align = Roo.bootstrap.Tooltip.alignment[placement];
23861         
23862         this.el.alignTo(this.bindEl, align[0],align[1]);
23863         //var arrow = this.el.select('.arrow',true).first();
23864         //arrow.set(align[2], 
23865         
23866         this.el.addClass(placement);
23867         
23868         this.el.addClass('in fade');
23869         
23870         this.hoverState = null;
23871         
23872         if (this.el.hasClass('fade')) {
23873             // fade it?
23874         }
23875         
23876     },
23877     hide : function()
23878     {
23879          
23880         if (!this.el) {
23881             return;
23882         }
23883         //this.el.setXY([0,0]);
23884         this.el.removeClass('in');
23885         //this.el.hide();
23886         
23887     }
23888     
23889 });
23890  
23891
23892  /*
23893  * - LGPL
23894  *
23895  * Location Picker
23896  * 
23897  */
23898
23899 /**
23900  * @class Roo.bootstrap.LocationPicker
23901  * @extends Roo.bootstrap.Component
23902  * Bootstrap LocationPicker class
23903  * @cfg {Number} latitude Position when init default 0
23904  * @cfg {Number} longitude Position when init default 0
23905  * @cfg {Number} zoom default 15
23906  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23907  * @cfg {Boolean} mapTypeControl default false
23908  * @cfg {Boolean} disableDoubleClickZoom default false
23909  * @cfg {Boolean} scrollwheel default true
23910  * @cfg {Boolean} streetViewControl default false
23911  * @cfg {Number} radius default 0
23912  * @cfg {String} locationName
23913  * @cfg {Boolean} draggable default true
23914  * @cfg {Boolean} enableAutocomplete default false
23915  * @cfg {Boolean} enableReverseGeocode default true
23916  * @cfg {String} markerTitle
23917  * 
23918  * @constructor
23919  * Create a new LocationPicker
23920  * @param {Object} config The config object
23921  */
23922
23923
23924 Roo.bootstrap.LocationPicker = function(config){
23925     
23926     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23927     
23928     this.addEvents({
23929         /**
23930          * @event initial
23931          * Fires when the picker initialized.
23932          * @param {Roo.bootstrap.LocationPicker} this
23933          * @param {Google Location} location
23934          */
23935         initial : true,
23936         /**
23937          * @event positionchanged
23938          * Fires when the picker position changed.
23939          * @param {Roo.bootstrap.LocationPicker} this
23940          * @param {Google Location} location
23941          */
23942         positionchanged : true,
23943         /**
23944          * @event resize
23945          * Fires when the map resize.
23946          * @param {Roo.bootstrap.LocationPicker} this
23947          */
23948         resize : true,
23949         /**
23950          * @event show
23951          * Fires when the map show.
23952          * @param {Roo.bootstrap.LocationPicker} this
23953          */
23954         show : true,
23955         /**
23956          * @event hide
23957          * Fires when the map hide.
23958          * @param {Roo.bootstrap.LocationPicker} this
23959          */
23960         hide : true,
23961         /**
23962          * @event mapClick
23963          * Fires when click the map.
23964          * @param {Roo.bootstrap.LocationPicker} this
23965          * @param {Map event} e
23966          */
23967         mapClick : true,
23968         /**
23969          * @event mapRightClick
23970          * Fires when right click the map.
23971          * @param {Roo.bootstrap.LocationPicker} this
23972          * @param {Map event} e
23973          */
23974         mapRightClick : true,
23975         /**
23976          * @event markerClick
23977          * Fires when click the marker.
23978          * @param {Roo.bootstrap.LocationPicker} this
23979          * @param {Map event} e
23980          */
23981         markerClick : true,
23982         /**
23983          * @event markerRightClick
23984          * Fires when right click the marker.
23985          * @param {Roo.bootstrap.LocationPicker} this
23986          * @param {Map event} e
23987          */
23988         markerRightClick : true,
23989         /**
23990          * @event OverlayViewDraw
23991          * Fires when OverlayView Draw
23992          * @param {Roo.bootstrap.LocationPicker} this
23993          */
23994         OverlayViewDraw : true,
23995         /**
23996          * @event OverlayViewOnAdd
23997          * Fires when OverlayView Draw
23998          * @param {Roo.bootstrap.LocationPicker} this
23999          */
24000         OverlayViewOnAdd : true,
24001         /**
24002          * @event OverlayViewOnRemove
24003          * Fires when OverlayView Draw
24004          * @param {Roo.bootstrap.LocationPicker} this
24005          */
24006         OverlayViewOnRemove : true,
24007         /**
24008          * @event OverlayViewShow
24009          * Fires when OverlayView Draw
24010          * @param {Roo.bootstrap.LocationPicker} this
24011          * @param {Pixel} cpx
24012          */
24013         OverlayViewShow : true,
24014         /**
24015          * @event OverlayViewHide
24016          * Fires when OverlayView Draw
24017          * @param {Roo.bootstrap.LocationPicker} this
24018          */
24019         OverlayViewHide : true,
24020         /**
24021          * @event loadexception
24022          * Fires when load google lib failed.
24023          * @param {Roo.bootstrap.LocationPicker} this
24024          */
24025         loadexception : true
24026     });
24027         
24028 };
24029
24030 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24031     
24032     gMapContext: false,
24033     
24034     latitude: 0,
24035     longitude: 0,
24036     zoom: 15,
24037     mapTypeId: false,
24038     mapTypeControl: false,
24039     disableDoubleClickZoom: false,
24040     scrollwheel: true,
24041     streetViewControl: false,
24042     radius: 0,
24043     locationName: '',
24044     draggable: true,
24045     enableAutocomplete: false,
24046     enableReverseGeocode: true,
24047     markerTitle: '',
24048     
24049     getAutoCreate: function()
24050     {
24051
24052         var cfg = {
24053             tag: 'div',
24054             cls: 'roo-location-picker'
24055         };
24056         
24057         return cfg
24058     },
24059     
24060     initEvents: function(ct, position)
24061     {       
24062         if(!this.el.getWidth() || this.isApplied()){
24063             return;
24064         }
24065         
24066         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24067         
24068         this.initial();
24069     },
24070     
24071     initial: function()
24072     {
24073         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24074             this.fireEvent('loadexception', this);
24075             return;
24076         }
24077         
24078         if(!this.mapTypeId){
24079             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24080         }
24081         
24082         this.gMapContext = this.GMapContext();
24083         
24084         this.initOverlayView();
24085         
24086         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24087         
24088         var _this = this;
24089                 
24090         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24091             _this.setPosition(_this.gMapContext.marker.position);
24092         });
24093         
24094         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24095             _this.fireEvent('mapClick', this, event);
24096             
24097         });
24098
24099         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24100             _this.fireEvent('mapRightClick', this, event);
24101             
24102         });
24103         
24104         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24105             _this.fireEvent('markerClick', this, event);
24106             
24107         });
24108
24109         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24110             _this.fireEvent('markerRightClick', this, event);
24111             
24112         });
24113         
24114         this.setPosition(this.gMapContext.location);
24115         
24116         this.fireEvent('initial', this, this.gMapContext.location);
24117     },
24118     
24119     initOverlayView: function()
24120     {
24121         var _this = this;
24122         
24123         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24124             
24125             draw: function()
24126             {
24127                 _this.fireEvent('OverlayViewDraw', _this);
24128             },
24129             
24130             onAdd: function()
24131             {
24132                 _this.fireEvent('OverlayViewOnAdd', _this);
24133             },
24134             
24135             onRemove: function()
24136             {
24137                 _this.fireEvent('OverlayViewOnRemove', _this);
24138             },
24139             
24140             show: function(cpx)
24141             {
24142                 _this.fireEvent('OverlayViewShow', _this, cpx);
24143             },
24144             
24145             hide: function()
24146             {
24147                 _this.fireEvent('OverlayViewHide', _this);
24148             }
24149             
24150         });
24151     },
24152     
24153     fromLatLngToContainerPixel: function(event)
24154     {
24155         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24156     },
24157     
24158     isApplied: function() 
24159     {
24160         return this.getGmapContext() == false ? false : true;
24161     },
24162     
24163     getGmapContext: function() 
24164     {
24165         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24166     },
24167     
24168     GMapContext: function() 
24169     {
24170         var position = new google.maps.LatLng(this.latitude, this.longitude);
24171         
24172         var _map = new google.maps.Map(this.el.dom, {
24173             center: position,
24174             zoom: this.zoom,
24175             mapTypeId: this.mapTypeId,
24176             mapTypeControl: this.mapTypeControl,
24177             disableDoubleClickZoom: this.disableDoubleClickZoom,
24178             scrollwheel: this.scrollwheel,
24179             streetViewControl: this.streetViewControl,
24180             locationName: this.locationName,
24181             draggable: this.draggable,
24182             enableAutocomplete: this.enableAutocomplete,
24183             enableReverseGeocode: this.enableReverseGeocode
24184         });
24185         
24186         var _marker = new google.maps.Marker({
24187             position: position,
24188             map: _map,
24189             title: this.markerTitle,
24190             draggable: this.draggable
24191         });
24192         
24193         return {
24194             map: _map,
24195             marker: _marker,
24196             circle: null,
24197             location: position,
24198             radius: this.radius,
24199             locationName: this.locationName,
24200             addressComponents: {
24201                 formatted_address: null,
24202                 addressLine1: null,
24203                 addressLine2: null,
24204                 streetName: null,
24205                 streetNumber: null,
24206                 city: null,
24207                 district: null,
24208                 state: null,
24209                 stateOrProvince: null
24210             },
24211             settings: this,
24212             domContainer: this.el.dom,
24213             geodecoder: new google.maps.Geocoder()
24214         };
24215     },
24216     
24217     drawCircle: function(center, radius, options) 
24218     {
24219         if (this.gMapContext.circle != null) {
24220             this.gMapContext.circle.setMap(null);
24221         }
24222         if (radius > 0) {
24223             radius *= 1;
24224             options = Roo.apply({}, options, {
24225                 strokeColor: "#0000FF",
24226                 strokeOpacity: .35,
24227                 strokeWeight: 2,
24228                 fillColor: "#0000FF",
24229                 fillOpacity: .2
24230             });
24231             
24232             options.map = this.gMapContext.map;
24233             options.radius = radius;
24234             options.center = center;
24235             this.gMapContext.circle = new google.maps.Circle(options);
24236             return this.gMapContext.circle;
24237         }
24238         
24239         return null;
24240     },
24241     
24242     setPosition: function(location) 
24243     {
24244         this.gMapContext.location = location;
24245         this.gMapContext.marker.setPosition(location);
24246         this.gMapContext.map.panTo(location);
24247         this.drawCircle(location, this.gMapContext.radius, {});
24248         
24249         var _this = this;
24250         
24251         if (this.gMapContext.settings.enableReverseGeocode) {
24252             this.gMapContext.geodecoder.geocode({
24253                 latLng: this.gMapContext.location
24254             }, function(results, status) {
24255                 
24256                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24257                     _this.gMapContext.locationName = results[0].formatted_address;
24258                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24259                     
24260                     _this.fireEvent('positionchanged', this, location);
24261                 }
24262             });
24263             
24264             return;
24265         }
24266         
24267         this.fireEvent('positionchanged', this, location);
24268     },
24269     
24270     resize: function()
24271     {
24272         google.maps.event.trigger(this.gMapContext.map, "resize");
24273         
24274         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24275         
24276         this.fireEvent('resize', this);
24277     },
24278     
24279     setPositionByLatLng: function(latitude, longitude)
24280     {
24281         this.setPosition(new google.maps.LatLng(latitude, longitude));
24282     },
24283     
24284     getCurrentPosition: function() 
24285     {
24286         return {
24287             latitude: this.gMapContext.location.lat(),
24288             longitude: this.gMapContext.location.lng()
24289         };
24290     },
24291     
24292     getAddressName: function() 
24293     {
24294         return this.gMapContext.locationName;
24295     },
24296     
24297     getAddressComponents: function() 
24298     {
24299         return this.gMapContext.addressComponents;
24300     },
24301     
24302     address_component_from_google_geocode: function(address_components) 
24303     {
24304         var result = {};
24305         
24306         for (var i = 0; i < address_components.length; i++) {
24307             var component = address_components[i];
24308             if (component.types.indexOf("postal_code") >= 0) {
24309                 result.postalCode = component.short_name;
24310             } else if (component.types.indexOf("street_number") >= 0) {
24311                 result.streetNumber = component.short_name;
24312             } else if (component.types.indexOf("route") >= 0) {
24313                 result.streetName = component.short_name;
24314             } else if (component.types.indexOf("neighborhood") >= 0) {
24315                 result.city = component.short_name;
24316             } else if (component.types.indexOf("locality") >= 0) {
24317                 result.city = component.short_name;
24318             } else if (component.types.indexOf("sublocality") >= 0) {
24319                 result.district = component.short_name;
24320             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24321                 result.stateOrProvince = component.short_name;
24322             } else if (component.types.indexOf("country") >= 0) {
24323                 result.country = component.short_name;
24324             }
24325         }
24326         
24327         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24328         result.addressLine2 = "";
24329         return result;
24330     },
24331     
24332     setZoomLevel: function(zoom)
24333     {
24334         this.gMapContext.map.setZoom(zoom);
24335     },
24336     
24337     show: function()
24338     {
24339         if(!this.el){
24340             return;
24341         }
24342         
24343         this.el.show();
24344         
24345         this.resize();
24346         
24347         this.fireEvent('show', this);
24348     },
24349     
24350     hide: function()
24351     {
24352         if(!this.el){
24353             return;
24354         }
24355         
24356         this.el.hide();
24357         
24358         this.fireEvent('hide', this);
24359     }
24360     
24361 });
24362
24363 Roo.apply(Roo.bootstrap.LocationPicker, {
24364     
24365     OverlayView : function(map, options)
24366     {
24367         options = options || {};
24368         
24369         this.setMap(map);
24370     }
24371     
24372     
24373 });/*
24374  * - LGPL
24375  *
24376  * Alert
24377  * 
24378  */
24379
24380 /**
24381  * @class Roo.bootstrap.Alert
24382  * @extends Roo.bootstrap.Component
24383  * Bootstrap Alert class
24384  * @cfg {String} title The title of alert
24385  * @cfg {String} html The content of alert
24386  * @cfg {String} weight (  success | info | warning | danger )
24387  * @cfg {String} faicon font-awesomeicon
24388  * 
24389  * @constructor
24390  * Create a new alert
24391  * @param {Object} config The config object
24392  */
24393
24394
24395 Roo.bootstrap.Alert = function(config){
24396     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24397     
24398 };
24399
24400 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24401     
24402     title: '',
24403     html: '',
24404     weight: false,
24405     faicon: false,
24406     
24407     getAutoCreate : function()
24408     {
24409         
24410         var cfg = {
24411             tag : 'div',
24412             cls : 'alert',
24413             cn : [
24414                 {
24415                     tag : 'i',
24416                     cls : 'roo-alert-icon'
24417                     
24418                 },
24419                 {
24420                     tag : 'b',
24421                     cls : 'roo-alert-title',
24422                     html : this.title
24423                 },
24424                 {
24425                     tag : 'span',
24426                     cls : 'roo-alert-text',
24427                     html : this.html
24428                 }
24429             ]
24430         };
24431         
24432         if(this.faicon){
24433             cfg.cn[0].cls += ' fa ' + this.faicon;
24434         }
24435         
24436         if(this.weight){
24437             cfg.cls += ' alert-' + this.weight;
24438         }
24439         
24440         return cfg;
24441     },
24442     
24443     initEvents: function() 
24444     {
24445         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24446     },
24447     
24448     setTitle : function(str)
24449     {
24450         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24451     },
24452     
24453     setText : function(str)
24454     {
24455         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24456     },
24457     
24458     setWeight : function(weight)
24459     {
24460         if(this.weight){
24461             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24462         }
24463         
24464         this.weight = weight;
24465         
24466         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24467     },
24468     
24469     setIcon : function(icon)
24470     {
24471         if(this.faicon){
24472             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24473         }
24474         
24475         this.faicon = icon;
24476         
24477         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24478     },
24479     
24480     hide: function() 
24481     {
24482         this.el.hide();   
24483     },
24484     
24485     show: function() 
24486     {  
24487         this.el.show();   
24488     }
24489     
24490 });
24491
24492  
24493 /*
24494 * Licence: LGPL
24495 */
24496
24497 /**
24498  * @class Roo.bootstrap.UploadCropbox
24499  * @extends Roo.bootstrap.Component
24500  * Bootstrap UploadCropbox class
24501  * @cfg {String} emptyText show when image has been loaded
24502  * @cfg {String} rotateNotify show when image too small to rotate
24503  * @cfg {Number} errorTimeout default 3000
24504  * @cfg {Number} minWidth default 300
24505  * @cfg {Number} minHeight default 300
24506  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24507  * @cfg {Boolean} isDocument (true|false) default false
24508  * @cfg {String} url action url
24509  * @cfg {String} paramName default 'imageUpload'
24510  * @cfg {String} method default POST
24511  * @cfg {Boolean} loadMask (true|false) default true
24512  * @cfg {Boolean} loadingText default 'Loading...'
24513  * 
24514  * @constructor
24515  * Create a new UploadCropbox
24516  * @param {Object} config The config object
24517  */
24518
24519 Roo.bootstrap.UploadCropbox = function(config){
24520     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24521     
24522     this.addEvents({
24523         /**
24524          * @event beforeselectfile
24525          * Fire before select file
24526          * @param {Roo.bootstrap.UploadCropbox} this
24527          */
24528         "beforeselectfile" : true,
24529         /**
24530          * @event initial
24531          * Fire after initEvent
24532          * @param {Roo.bootstrap.UploadCropbox} this
24533          */
24534         "initial" : true,
24535         /**
24536          * @event crop
24537          * Fire after initEvent
24538          * @param {Roo.bootstrap.UploadCropbox} this
24539          * @param {String} data
24540          */
24541         "crop" : true,
24542         /**
24543          * @event prepare
24544          * Fire when preparing the file data
24545          * @param {Roo.bootstrap.UploadCropbox} this
24546          * @param {Object} file
24547          */
24548         "prepare" : true,
24549         /**
24550          * @event exception
24551          * Fire when get exception
24552          * @param {Roo.bootstrap.UploadCropbox} this
24553          * @param {XMLHttpRequest} xhr
24554          */
24555         "exception" : true,
24556         /**
24557          * @event beforeloadcanvas
24558          * Fire before load the canvas
24559          * @param {Roo.bootstrap.UploadCropbox} this
24560          * @param {String} src
24561          */
24562         "beforeloadcanvas" : true,
24563         /**
24564          * @event trash
24565          * Fire when trash image
24566          * @param {Roo.bootstrap.UploadCropbox} this
24567          */
24568         "trash" : true,
24569         /**
24570          * @event download
24571          * Fire when download the image
24572          * @param {Roo.bootstrap.UploadCropbox} this
24573          */
24574         "download" : true,
24575         /**
24576          * @event footerbuttonclick
24577          * Fire when footerbuttonclick
24578          * @param {Roo.bootstrap.UploadCropbox} this
24579          * @param {String} type
24580          */
24581         "footerbuttonclick" : true,
24582         /**
24583          * @event resize
24584          * Fire when resize
24585          * @param {Roo.bootstrap.UploadCropbox} this
24586          */
24587         "resize" : true,
24588         /**
24589          * @event rotate
24590          * Fire when rotate the image
24591          * @param {Roo.bootstrap.UploadCropbox} this
24592          * @param {String} pos
24593          */
24594         "rotate" : true,
24595         /**
24596          * @event inspect
24597          * Fire when inspect the file
24598          * @param {Roo.bootstrap.UploadCropbox} this
24599          * @param {Object} file
24600          */
24601         "inspect" : true,
24602         /**
24603          * @event upload
24604          * Fire when xhr upload the file
24605          * @param {Roo.bootstrap.UploadCropbox} this
24606          * @param {Object} data
24607          */
24608         "upload" : true,
24609         /**
24610          * @event arrange
24611          * Fire when arrange the file data
24612          * @param {Roo.bootstrap.UploadCropbox} this
24613          * @param {Object} formData
24614          */
24615         "arrange" : true
24616     });
24617     
24618     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24619 };
24620
24621 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24622     
24623     emptyText : 'Click to upload image',
24624     rotateNotify : 'Image is too small to rotate',
24625     errorTimeout : 3000,
24626     scale : 0,
24627     baseScale : 1,
24628     rotate : 0,
24629     dragable : false,
24630     pinching : false,
24631     mouseX : 0,
24632     mouseY : 0,
24633     cropData : false,
24634     minWidth : 300,
24635     minHeight : 300,
24636     file : false,
24637     exif : {},
24638     baseRotate : 1,
24639     cropType : 'image/jpeg',
24640     buttons : false,
24641     canvasLoaded : false,
24642     isDocument : false,
24643     method : 'POST',
24644     paramName : 'imageUpload',
24645     loadMask : true,
24646     loadingText : 'Loading...',
24647     maskEl : false,
24648     
24649     getAutoCreate : function()
24650     {
24651         var cfg = {
24652             tag : 'div',
24653             cls : 'roo-upload-cropbox',
24654             cn : [
24655                 {
24656                     tag : 'input',
24657                     cls : 'roo-upload-cropbox-selector',
24658                     type : 'file'
24659                 },
24660                 {
24661                     tag : 'div',
24662                     cls : 'roo-upload-cropbox-body',
24663                     style : 'cursor:pointer',
24664                     cn : [
24665                         {
24666                             tag : 'div',
24667                             cls : 'roo-upload-cropbox-preview'
24668                         },
24669                         {
24670                             tag : 'div',
24671                             cls : 'roo-upload-cropbox-thumb'
24672                         },
24673                         {
24674                             tag : 'div',
24675                             cls : 'roo-upload-cropbox-empty-notify',
24676                             html : this.emptyText
24677                         },
24678                         {
24679                             tag : 'div',
24680                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24681                             html : this.rotateNotify
24682                         }
24683                     ]
24684                 },
24685                 {
24686                     tag : 'div',
24687                     cls : 'roo-upload-cropbox-footer',
24688                     cn : {
24689                         tag : 'div',
24690                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24691                         cn : []
24692                     }
24693                 }
24694             ]
24695         };
24696         
24697         return cfg;
24698     },
24699     
24700     onRender : function(ct, position)
24701     {
24702         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24703         
24704         if (this.buttons.length) {
24705             
24706             Roo.each(this.buttons, function(bb) {
24707                 
24708                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24709                 
24710                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24711                 
24712             }, this);
24713         }
24714         
24715         if(this.loadMask){
24716             this.maskEl = this.el;
24717         }
24718     },
24719     
24720     initEvents : function()
24721     {
24722         this.urlAPI = (window.createObjectURL && window) || 
24723                                 (window.URL && URL.revokeObjectURL && URL) || 
24724                                 (window.webkitURL && webkitURL);
24725                         
24726         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24727         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24728         
24729         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24730         this.selectorEl.hide();
24731         
24732         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24733         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24734         
24735         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24736         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24737         this.thumbEl.hide();
24738         
24739         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24740         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24741         
24742         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24743         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24744         this.errorEl.hide();
24745         
24746         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24747         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24748         this.footerEl.hide();
24749         
24750         this.setThumbBoxSize();
24751         
24752         this.bind();
24753         
24754         this.resize();
24755         
24756         this.fireEvent('initial', this);
24757     },
24758
24759     bind : function()
24760     {
24761         var _this = this;
24762         
24763         window.addEventListener("resize", function() { _this.resize(); } );
24764         
24765         this.bodyEl.on('click', this.beforeSelectFile, this);
24766         
24767         if(Roo.isTouch){
24768             this.bodyEl.on('touchstart', this.onTouchStart, this);
24769             this.bodyEl.on('touchmove', this.onTouchMove, this);
24770             this.bodyEl.on('touchend', this.onTouchEnd, this);
24771         }
24772         
24773         if(!Roo.isTouch){
24774             this.bodyEl.on('mousedown', this.onMouseDown, this);
24775             this.bodyEl.on('mousemove', this.onMouseMove, this);
24776             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24777             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24778             Roo.get(document).on('mouseup', this.onMouseUp, this);
24779         }
24780         
24781         this.selectorEl.on('change', this.onFileSelected, this);
24782     },
24783     
24784     reset : function()
24785     {    
24786         this.scale = 0;
24787         this.baseScale = 1;
24788         this.rotate = 0;
24789         this.baseRotate = 1;
24790         this.dragable = false;
24791         this.pinching = false;
24792         this.mouseX = 0;
24793         this.mouseY = 0;
24794         this.cropData = false;
24795         this.notifyEl.dom.innerHTML = this.emptyText;
24796         
24797         this.selectorEl.dom.value = '';
24798         
24799     },
24800     
24801     resize : function()
24802     {
24803         if(this.fireEvent('resize', this) != false){
24804             this.setThumbBoxPosition();
24805             this.setCanvasPosition();
24806         }
24807     },
24808     
24809     onFooterButtonClick : function(e, el, o, type)
24810     {
24811         switch (type) {
24812             case 'rotate-left' :
24813                 this.onRotateLeft(e);
24814                 break;
24815             case 'rotate-right' :
24816                 this.onRotateRight(e);
24817                 break;
24818             case 'picture' :
24819                 this.beforeSelectFile(e);
24820                 break;
24821             case 'trash' :
24822                 this.trash(e);
24823                 break;
24824             case 'crop' :
24825                 this.crop(e);
24826                 break;
24827             case 'download' :
24828                 this.download(e);
24829                 break;
24830             default :
24831                 break;
24832         }
24833         
24834         this.fireEvent('footerbuttonclick', this, type);
24835     },
24836     
24837     beforeSelectFile : function(e)
24838     {
24839         e.preventDefault();
24840         
24841         if(this.fireEvent('beforeselectfile', this) != false){
24842             this.selectorEl.dom.click();
24843         }
24844     },
24845     
24846     onFileSelected : function(e)
24847     {
24848         e.preventDefault();
24849         
24850         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24851             return;
24852         }
24853         
24854         var file = this.selectorEl.dom.files[0];
24855         
24856         if(this.fireEvent('inspect', this, file) != false){
24857             this.prepare(file);
24858         }
24859         
24860     },
24861     
24862     trash : function(e)
24863     {
24864         this.fireEvent('trash', this);
24865     },
24866     
24867     download : function(e)
24868     {
24869         this.fireEvent('download', this);
24870     },
24871     
24872     loadCanvas : function(src)
24873     {   
24874         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24875             
24876             this.reset();
24877             
24878             this.imageEl = document.createElement('img');
24879             
24880             var _this = this;
24881             
24882             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24883             
24884             this.imageEl.src = src;
24885         }
24886     },
24887     
24888     onLoadCanvas : function()
24889     {   
24890         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24891         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24892         
24893         this.bodyEl.un('click', this.beforeSelectFile, this);
24894         
24895         this.notifyEl.hide();
24896         this.thumbEl.show();
24897         this.footerEl.show();
24898         
24899         this.baseRotateLevel();
24900         
24901         if(this.isDocument){
24902             this.setThumbBoxSize();
24903         }
24904         
24905         this.setThumbBoxPosition();
24906         
24907         this.baseScaleLevel();
24908         
24909         this.draw();
24910         
24911         this.resize();
24912         
24913         this.canvasLoaded = true;
24914         
24915         if(this.loadMask){
24916             this.maskEl.unmask();
24917         }
24918         
24919     },
24920     
24921     setCanvasPosition : function()
24922     {   
24923         if(!this.canvasEl){
24924             return;
24925         }
24926         
24927         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24928         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24929         
24930         this.previewEl.setLeft(pw);
24931         this.previewEl.setTop(ph);
24932         
24933     },
24934     
24935     onMouseDown : function(e)
24936     {   
24937         e.stopEvent();
24938         
24939         this.dragable = true;
24940         this.pinching = false;
24941         
24942         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24943             this.dragable = false;
24944             return;
24945         }
24946         
24947         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24948         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24949         
24950     },
24951     
24952     onMouseMove : function(e)
24953     {   
24954         e.stopEvent();
24955         
24956         if(!this.canvasLoaded){
24957             return;
24958         }
24959         
24960         if (!this.dragable){
24961             return;
24962         }
24963         
24964         var minX = Math.ceil(this.thumbEl.getLeft(true));
24965         var minY = Math.ceil(this.thumbEl.getTop(true));
24966         
24967         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24968         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24969         
24970         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24971         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24972         
24973         x = x - this.mouseX;
24974         y = y - this.mouseY;
24975         
24976         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24977         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24978         
24979         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24980         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24981         
24982         this.previewEl.setLeft(bgX);
24983         this.previewEl.setTop(bgY);
24984         
24985         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24986         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24987     },
24988     
24989     onMouseUp : function(e)
24990     {   
24991         e.stopEvent();
24992         
24993         this.dragable = false;
24994     },
24995     
24996     onMouseWheel : function(e)
24997     {   
24998         e.stopEvent();
24999         
25000         this.startScale = this.scale;
25001         
25002         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25003         
25004         if(!this.zoomable()){
25005             this.scale = this.startScale;
25006             return;
25007         }
25008         
25009         this.draw();
25010         
25011         return;
25012     },
25013     
25014     zoomable : function()
25015     {
25016         var minScale = this.thumbEl.getWidth() / this.minWidth;
25017         
25018         if(this.minWidth < this.minHeight){
25019             minScale = this.thumbEl.getHeight() / this.minHeight;
25020         }
25021         
25022         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25023         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25024         
25025         if(
25026                 this.isDocument &&
25027                 (this.rotate == 0 || this.rotate == 180) && 
25028                 (
25029                     width > this.imageEl.OriginWidth || 
25030                     height > this.imageEl.OriginHeight ||
25031                     (width < this.minWidth && height < this.minHeight)
25032                 )
25033         ){
25034             return false;
25035         }
25036         
25037         if(
25038                 this.isDocument &&
25039                 (this.rotate == 90 || this.rotate == 270) && 
25040                 (
25041                     width > this.imageEl.OriginWidth || 
25042                     height > this.imageEl.OriginHeight ||
25043                     (width < this.minHeight && height < this.minWidth)
25044                 )
25045         ){
25046             return false;
25047         }
25048         
25049         if(
25050                 !this.isDocument &&
25051                 (this.rotate == 0 || this.rotate == 180) && 
25052                 (
25053                     width < this.minWidth || 
25054                     width > this.imageEl.OriginWidth || 
25055                     height < this.minHeight || 
25056                     height > this.imageEl.OriginHeight
25057                 )
25058         ){
25059             return false;
25060         }
25061         
25062         if(
25063                 !this.isDocument &&
25064                 (this.rotate == 90 || this.rotate == 270) && 
25065                 (
25066                     width < this.minHeight || 
25067                     width > this.imageEl.OriginWidth || 
25068                     height < this.minWidth || 
25069                     height > this.imageEl.OriginHeight
25070                 )
25071         ){
25072             return false;
25073         }
25074         
25075         return true;
25076         
25077     },
25078     
25079     onRotateLeft : function(e)
25080     {   
25081         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25082             
25083             var minScale = this.thumbEl.getWidth() / this.minWidth;
25084             
25085             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25086             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25087             
25088             this.startScale = this.scale;
25089             
25090             while (this.getScaleLevel() < minScale){
25091             
25092                 this.scale = this.scale + 1;
25093                 
25094                 if(!this.zoomable()){
25095                     break;
25096                 }
25097                 
25098                 if(
25099                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25100                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25101                 ){
25102                     continue;
25103                 }
25104                 
25105                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25106
25107                 this.draw();
25108                 
25109                 return;
25110             }
25111             
25112             this.scale = this.startScale;
25113             
25114             this.onRotateFail();
25115             
25116             return false;
25117         }
25118         
25119         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25120
25121         if(this.isDocument){
25122             this.setThumbBoxSize();
25123             this.setThumbBoxPosition();
25124             this.setCanvasPosition();
25125         }
25126         
25127         this.draw();
25128         
25129         this.fireEvent('rotate', this, 'left');
25130         
25131     },
25132     
25133     onRotateRight : function(e)
25134     {
25135         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25136             
25137             var minScale = this.thumbEl.getWidth() / this.minWidth;
25138         
25139             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25140             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25141             
25142             this.startScale = this.scale;
25143             
25144             while (this.getScaleLevel() < minScale){
25145             
25146                 this.scale = this.scale + 1;
25147                 
25148                 if(!this.zoomable()){
25149                     break;
25150                 }
25151                 
25152                 if(
25153                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25154                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25155                 ){
25156                     continue;
25157                 }
25158                 
25159                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25160
25161                 this.draw();
25162                 
25163                 return;
25164             }
25165             
25166             this.scale = this.startScale;
25167             
25168             this.onRotateFail();
25169             
25170             return false;
25171         }
25172         
25173         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25174
25175         if(this.isDocument){
25176             this.setThumbBoxSize();
25177             this.setThumbBoxPosition();
25178             this.setCanvasPosition();
25179         }
25180         
25181         this.draw();
25182         
25183         this.fireEvent('rotate', this, 'right');
25184     },
25185     
25186     onRotateFail : function()
25187     {
25188         this.errorEl.show(true);
25189         
25190         var _this = this;
25191         
25192         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25193     },
25194     
25195     draw : function()
25196     {
25197         this.previewEl.dom.innerHTML = '';
25198         
25199         var canvasEl = document.createElement("canvas");
25200         
25201         var contextEl = canvasEl.getContext("2d");
25202         
25203         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25204         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25205         var center = this.imageEl.OriginWidth / 2;
25206         
25207         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25208             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25209             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25210             center = this.imageEl.OriginHeight / 2;
25211         }
25212         
25213         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25214         
25215         contextEl.translate(center, center);
25216         contextEl.rotate(this.rotate * Math.PI / 180);
25217
25218         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25219         
25220         this.canvasEl = document.createElement("canvas");
25221         
25222         this.contextEl = this.canvasEl.getContext("2d");
25223         
25224         switch (this.rotate) {
25225             case 0 :
25226                 
25227                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25228                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25229                 
25230                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25231                 
25232                 break;
25233             case 90 : 
25234                 
25235                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25236                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25237                 
25238                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25239                     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);
25240                     break;
25241                 }
25242                 
25243                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25244                 
25245                 break;
25246             case 180 :
25247                 
25248                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25249                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25250                 
25251                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25252                     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);
25253                     break;
25254                 }
25255                 
25256                 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);
25257                 
25258                 break;
25259             case 270 :
25260                 
25261                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25262                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25263         
25264                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25265                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25266                     break;
25267                 }
25268                 
25269                 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);
25270                 
25271                 break;
25272             default : 
25273                 break;
25274         }
25275         
25276         this.previewEl.appendChild(this.canvasEl);
25277         
25278         this.setCanvasPosition();
25279     },
25280     
25281     crop : function()
25282     {
25283         if(!this.canvasLoaded){
25284             return;
25285         }
25286         
25287         var imageCanvas = document.createElement("canvas");
25288         
25289         var imageContext = imageCanvas.getContext("2d");
25290         
25291         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25292         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25293         
25294         var center = imageCanvas.width / 2;
25295         
25296         imageContext.translate(center, center);
25297         
25298         imageContext.rotate(this.rotate * Math.PI / 180);
25299         
25300         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25301         
25302         var canvas = document.createElement("canvas");
25303         
25304         var context = canvas.getContext("2d");
25305                 
25306         canvas.width = this.minWidth;
25307         canvas.height = this.minHeight;
25308
25309         switch (this.rotate) {
25310             case 0 :
25311                 
25312                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25313                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25314                 
25315                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25316                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25317                 
25318                 var targetWidth = this.minWidth - 2 * x;
25319                 var targetHeight = this.minHeight - 2 * y;
25320                 
25321                 var scale = 1;
25322                 
25323                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25324                     scale = targetWidth / width;
25325                 }
25326                 
25327                 if(x > 0 && y == 0){
25328                     scale = targetHeight / height;
25329                 }
25330                 
25331                 if(x > 0 && y > 0){
25332                     scale = targetWidth / width;
25333                     
25334                     if(width < height){
25335                         scale = targetHeight / height;
25336                     }
25337                 }
25338                 
25339                 context.scale(scale, scale);
25340                 
25341                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25342                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25343
25344                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25345                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25346
25347                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25348                 
25349                 break;
25350             case 90 : 
25351                 
25352                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25353                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25354                 
25355                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25356                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25357                 
25358                 var targetWidth = this.minWidth - 2 * x;
25359                 var targetHeight = this.minHeight - 2 * y;
25360                 
25361                 var scale = 1;
25362                 
25363                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25364                     scale = targetWidth / width;
25365                 }
25366                 
25367                 if(x > 0 && y == 0){
25368                     scale = targetHeight / height;
25369                 }
25370                 
25371                 if(x > 0 && y > 0){
25372                     scale = targetWidth / width;
25373                     
25374                     if(width < height){
25375                         scale = targetHeight / height;
25376                     }
25377                 }
25378                 
25379                 context.scale(scale, scale);
25380                 
25381                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25382                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25383
25384                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25385                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25386                 
25387                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25388                 
25389                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25390                 
25391                 break;
25392             case 180 :
25393                 
25394                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25395                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25396                 
25397                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25398                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25399                 
25400                 var targetWidth = this.minWidth - 2 * x;
25401                 var targetHeight = this.minHeight - 2 * y;
25402                 
25403                 var scale = 1;
25404                 
25405                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25406                     scale = targetWidth / width;
25407                 }
25408                 
25409                 if(x > 0 && y == 0){
25410                     scale = targetHeight / height;
25411                 }
25412                 
25413                 if(x > 0 && y > 0){
25414                     scale = targetWidth / width;
25415                     
25416                     if(width < height){
25417                         scale = targetHeight / height;
25418                     }
25419                 }
25420                 
25421                 context.scale(scale, scale);
25422                 
25423                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25424                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25425
25426                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25427                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25428
25429                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25430                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25431                 
25432                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25433                 
25434                 break;
25435             case 270 :
25436                 
25437                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25438                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25439                 
25440                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25441                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25442                 
25443                 var targetWidth = this.minWidth - 2 * x;
25444                 var targetHeight = this.minHeight - 2 * y;
25445                 
25446                 var scale = 1;
25447                 
25448                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25449                     scale = targetWidth / width;
25450                 }
25451                 
25452                 if(x > 0 && y == 0){
25453                     scale = targetHeight / height;
25454                 }
25455                 
25456                 if(x > 0 && y > 0){
25457                     scale = targetWidth / width;
25458                     
25459                     if(width < height){
25460                         scale = targetHeight / height;
25461                     }
25462                 }
25463                 
25464                 context.scale(scale, scale);
25465                 
25466                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25467                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25468
25469                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25470                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25471                 
25472                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25473                 
25474                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25475                 
25476                 break;
25477             default : 
25478                 break;
25479         }
25480         
25481         this.cropData = canvas.toDataURL(this.cropType);
25482         
25483         if(this.fireEvent('crop', this, this.cropData) !== false){
25484             this.process(this.file, this.cropData);
25485         }
25486         
25487         return;
25488         
25489     },
25490     
25491     setThumbBoxSize : function()
25492     {
25493         var width, height;
25494         
25495         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25496             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25497             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25498             
25499             this.minWidth = width;
25500             this.minHeight = height;
25501             
25502             if(this.rotate == 90 || this.rotate == 270){
25503                 this.minWidth = height;
25504                 this.minHeight = width;
25505             }
25506         }
25507         
25508         height = 300;
25509         width = Math.ceil(this.minWidth * height / this.minHeight);
25510         
25511         if(this.minWidth > this.minHeight){
25512             width = 300;
25513             height = Math.ceil(this.minHeight * width / this.minWidth);
25514         }
25515         
25516         this.thumbEl.setStyle({
25517             width : width + 'px',
25518             height : height + 'px'
25519         });
25520
25521         return;
25522             
25523     },
25524     
25525     setThumbBoxPosition : function()
25526     {
25527         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25528         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25529         
25530         this.thumbEl.setLeft(x);
25531         this.thumbEl.setTop(y);
25532         
25533     },
25534     
25535     baseRotateLevel : function()
25536     {
25537         this.baseRotate = 1;
25538         
25539         if(
25540                 typeof(this.exif) != 'undefined' &&
25541                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25542                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25543         ){
25544             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25545         }
25546         
25547         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25548         
25549     },
25550     
25551     baseScaleLevel : function()
25552     {
25553         var width, height;
25554         
25555         if(this.isDocument){
25556             
25557             if(this.baseRotate == 6 || this.baseRotate == 8){
25558             
25559                 height = this.thumbEl.getHeight();
25560                 this.baseScale = height / this.imageEl.OriginWidth;
25561
25562                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25563                     width = this.thumbEl.getWidth();
25564                     this.baseScale = width / this.imageEl.OriginHeight;
25565                 }
25566
25567                 return;
25568             }
25569
25570             height = this.thumbEl.getHeight();
25571             this.baseScale = height / this.imageEl.OriginHeight;
25572
25573             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25574                 width = this.thumbEl.getWidth();
25575                 this.baseScale = width / this.imageEl.OriginWidth;
25576             }
25577
25578             return;
25579         }
25580         
25581         if(this.baseRotate == 6 || this.baseRotate == 8){
25582             
25583             width = this.thumbEl.getHeight();
25584             this.baseScale = width / this.imageEl.OriginHeight;
25585             
25586             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25587                 height = this.thumbEl.getWidth();
25588                 this.baseScale = height / this.imageEl.OriginHeight;
25589             }
25590             
25591             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25592                 height = this.thumbEl.getWidth();
25593                 this.baseScale = height / this.imageEl.OriginHeight;
25594                 
25595                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25596                     width = this.thumbEl.getHeight();
25597                     this.baseScale = width / this.imageEl.OriginWidth;
25598                 }
25599             }
25600             
25601             return;
25602         }
25603         
25604         width = this.thumbEl.getWidth();
25605         this.baseScale = width / this.imageEl.OriginWidth;
25606         
25607         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25608             height = this.thumbEl.getHeight();
25609             this.baseScale = height / this.imageEl.OriginHeight;
25610         }
25611         
25612         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25613             
25614             height = this.thumbEl.getHeight();
25615             this.baseScale = height / this.imageEl.OriginHeight;
25616             
25617             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25618                 width = this.thumbEl.getWidth();
25619                 this.baseScale = width / this.imageEl.OriginWidth;
25620             }
25621             
25622         }
25623         
25624         return;
25625     },
25626     
25627     getScaleLevel : function()
25628     {
25629         return this.baseScale * Math.pow(1.1, this.scale);
25630     },
25631     
25632     onTouchStart : function(e)
25633     {
25634         if(!this.canvasLoaded){
25635             this.beforeSelectFile(e);
25636             return;
25637         }
25638         
25639         var touches = e.browserEvent.touches;
25640         
25641         if(!touches){
25642             return;
25643         }
25644         
25645         if(touches.length == 1){
25646             this.onMouseDown(e);
25647             return;
25648         }
25649         
25650         if(touches.length != 2){
25651             return;
25652         }
25653         
25654         var coords = [];
25655         
25656         for(var i = 0, finger; finger = touches[i]; i++){
25657             coords.push(finger.pageX, finger.pageY);
25658         }
25659         
25660         var x = Math.pow(coords[0] - coords[2], 2);
25661         var y = Math.pow(coords[1] - coords[3], 2);
25662         
25663         this.startDistance = Math.sqrt(x + y);
25664         
25665         this.startScale = this.scale;
25666         
25667         this.pinching = true;
25668         this.dragable = false;
25669         
25670     },
25671     
25672     onTouchMove : function(e)
25673     {
25674         if(!this.pinching && !this.dragable){
25675             return;
25676         }
25677         
25678         var touches = e.browserEvent.touches;
25679         
25680         if(!touches){
25681             return;
25682         }
25683         
25684         if(this.dragable){
25685             this.onMouseMove(e);
25686             return;
25687         }
25688         
25689         var coords = [];
25690         
25691         for(var i = 0, finger; finger = touches[i]; i++){
25692             coords.push(finger.pageX, finger.pageY);
25693         }
25694         
25695         var x = Math.pow(coords[0] - coords[2], 2);
25696         var y = Math.pow(coords[1] - coords[3], 2);
25697         
25698         this.endDistance = Math.sqrt(x + y);
25699         
25700         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25701         
25702         if(!this.zoomable()){
25703             this.scale = this.startScale;
25704             return;
25705         }
25706         
25707         this.draw();
25708         
25709     },
25710     
25711     onTouchEnd : function(e)
25712     {
25713         this.pinching = false;
25714         this.dragable = false;
25715         
25716     },
25717     
25718     process : function(file, crop)
25719     {
25720         if(this.loadMask){
25721             this.maskEl.mask(this.loadingText);
25722         }
25723         
25724         this.xhr = new XMLHttpRequest();
25725         
25726         file.xhr = this.xhr;
25727
25728         this.xhr.open(this.method, this.url, true);
25729         
25730         var headers = {
25731             "Accept": "application/json",
25732             "Cache-Control": "no-cache",
25733             "X-Requested-With": "XMLHttpRequest"
25734         };
25735         
25736         for (var headerName in headers) {
25737             var headerValue = headers[headerName];
25738             if (headerValue) {
25739                 this.xhr.setRequestHeader(headerName, headerValue);
25740             }
25741         }
25742         
25743         var _this = this;
25744         
25745         this.xhr.onload = function()
25746         {
25747             _this.xhrOnLoad(_this.xhr);
25748         }
25749         
25750         this.xhr.onerror = function()
25751         {
25752             _this.xhrOnError(_this.xhr);
25753         }
25754         
25755         var formData = new FormData();
25756
25757         formData.append('returnHTML', 'NO');
25758         
25759         if(crop){
25760             formData.append('crop', crop);
25761         }
25762         
25763         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25764             formData.append(this.paramName, file, file.name);
25765         }
25766         
25767         if(typeof(file.filename) != 'undefined'){
25768             formData.append('filename', file.filename);
25769         }
25770         
25771         if(typeof(file.mimetype) != 'undefined'){
25772             formData.append('mimetype', file.mimetype);
25773         }
25774         
25775         if(this.fireEvent('arrange', this, formData) != false){
25776             this.xhr.send(formData);
25777         };
25778     },
25779     
25780     xhrOnLoad : function(xhr)
25781     {
25782         if(this.loadMask){
25783             this.maskEl.unmask();
25784         }
25785         
25786         if (xhr.readyState !== 4) {
25787             this.fireEvent('exception', this, xhr);
25788             return;
25789         }
25790
25791         var response = Roo.decode(xhr.responseText);
25792         
25793         if(!response.success){
25794             this.fireEvent('exception', this, xhr);
25795             return;
25796         }
25797         
25798         var response = Roo.decode(xhr.responseText);
25799         
25800         this.fireEvent('upload', this, response);
25801         
25802     },
25803     
25804     xhrOnError : function()
25805     {
25806         if(this.loadMask){
25807             this.maskEl.unmask();
25808         }
25809         
25810         Roo.log('xhr on error');
25811         
25812         var response = Roo.decode(xhr.responseText);
25813           
25814         Roo.log(response);
25815         
25816     },
25817     
25818     prepare : function(file)
25819     {   
25820         if(this.loadMask){
25821             this.maskEl.mask(this.loadingText);
25822         }
25823         
25824         this.file = false;
25825         this.exif = {};
25826         
25827         if(typeof(file) === 'string'){
25828             this.loadCanvas(file);
25829             return;
25830         }
25831         
25832         if(!file || !this.urlAPI){
25833             return;
25834         }
25835         
25836         this.file = file;
25837         this.cropType = file.type;
25838         
25839         var _this = this;
25840         
25841         if(this.fireEvent('prepare', this, this.file) != false){
25842             
25843             var reader = new FileReader();
25844             
25845             reader.onload = function (e) {
25846                 if (e.target.error) {
25847                     Roo.log(e.target.error);
25848                     return;
25849                 }
25850                 
25851                 var buffer = e.target.result,
25852                     dataView = new DataView(buffer),
25853                     offset = 2,
25854                     maxOffset = dataView.byteLength - 4,
25855                     markerBytes,
25856                     markerLength;
25857                 
25858                 if (dataView.getUint16(0) === 0xffd8) {
25859                     while (offset < maxOffset) {
25860                         markerBytes = dataView.getUint16(offset);
25861                         
25862                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25863                             markerLength = dataView.getUint16(offset + 2) + 2;
25864                             if (offset + markerLength > dataView.byteLength) {
25865                                 Roo.log('Invalid meta data: Invalid segment size.');
25866                                 break;
25867                             }
25868                             
25869                             if(markerBytes == 0xffe1){
25870                                 _this.parseExifData(
25871                                     dataView,
25872                                     offset,
25873                                     markerLength
25874                                 );
25875                             }
25876                             
25877                             offset += markerLength;
25878                             
25879                             continue;
25880                         }
25881                         
25882                         break;
25883                     }
25884                     
25885                 }
25886                 
25887                 var url = _this.urlAPI.createObjectURL(_this.file);
25888                 
25889                 _this.loadCanvas(url);
25890                 
25891                 return;
25892             }
25893             
25894             reader.readAsArrayBuffer(this.file);
25895             
25896         }
25897         
25898     },
25899     
25900     parseExifData : function(dataView, offset, length)
25901     {
25902         var tiffOffset = offset + 10,
25903             littleEndian,
25904             dirOffset;
25905     
25906         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25907             // No Exif data, might be XMP data instead
25908             return;
25909         }
25910         
25911         // Check for the ASCII code for "Exif" (0x45786966):
25912         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25913             // No Exif data, might be XMP data instead
25914             return;
25915         }
25916         if (tiffOffset + 8 > dataView.byteLength) {
25917             Roo.log('Invalid Exif data: Invalid segment size.');
25918             return;
25919         }
25920         // Check for the two null bytes:
25921         if (dataView.getUint16(offset + 8) !== 0x0000) {
25922             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25923             return;
25924         }
25925         // Check the byte alignment:
25926         switch (dataView.getUint16(tiffOffset)) {
25927         case 0x4949:
25928             littleEndian = true;
25929             break;
25930         case 0x4D4D:
25931             littleEndian = false;
25932             break;
25933         default:
25934             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25935             return;
25936         }
25937         // Check for the TIFF tag marker (0x002A):
25938         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25939             Roo.log('Invalid Exif data: Missing TIFF marker.');
25940             return;
25941         }
25942         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25943         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25944         
25945         this.parseExifTags(
25946             dataView,
25947             tiffOffset,
25948             tiffOffset + dirOffset,
25949             littleEndian
25950         );
25951     },
25952     
25953     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25954     {
25955         var tagsNumber,
25956             dirEndOffset,
25957             i;
25958         if (dirOffset + 6 > dataView.byteLength) {
25959             Roo.log('Invalid Exif data: Invalid directory offset.');
25960             return;
25961         }
25962         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25963         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25964         if (dirEndOffset + 4 > dataView.byteLength) {
25965             Roo.log('Invalid Exif data: Invalid directory size.');
25966             return;
25967         }
25968         for (i = 0; i < tagsNumber; i += 1) {
25969             this.parseExifTag(
25970                 dataView,
25971                 tiffOffset,
25972                 dirOffset + 2 + 12 * i, // tag offset
25973                 littleEndian
25974             );
25975         }
25976         // Return the offset to the next directory:
25977         return dataView.getUint32(dirEndOffset, littleEndian);
25978     },
25979     
25980     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25981     {
25982         var tag = dataView.getUint16(offset, littleEndian);
25983         
25984         this.exif[tag] = this.getExifValue(
25985             dataView,
25986             tiffOffset,
25987             offset,
25988             dataView.getUint16(offset + 2, littleEndian), // tag type
25989             dataView.getUint32(offset + 4, littleEndian), // tag length
25990             littleEndian
25991         );
25992     },
25993     
25994     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25995     {
25996         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25997             tagSize,
25998             dataOffset,
25999             values,
26000             i,
26001             str,
26002             c;
26003     
26004         if (!tagType) {
26005             Roo.log('Invalid Exif data: Invalid tag type.');
26006             return;
26007         }
26008         
26009         tagSize = tagType.size * length;
26010         // Determine if the value is contained in the dataOffset bytes,
26011         // or if the value at the dataOffset is a pointer to the actual data:
26012         dataOffset = tagSize > 4 ?
26013                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26014         if (dataOffset + tagSize > dataView.byteLength) {
26015             Roo.log('Invalid Exif data: Invalid data offset.');
26016             return;
26017         }
26018         if (length === 1) {
26019             return tagType.getValue(dataView, dataOffset, littleEndian);
26020         }
26021         values = [];
26022         for (i = 0; i < length; i += 1) {
26023             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26024         }
26025         
26026         if (tagType.ascii) {
26027             str = '';
26028             // Concatenate the chars:
26029             for (i = 0; i < values.length; i += 1) {
26030                 c = values[i];
26031                 // Ignore the terminating NULL byte(s):
26032                 if (c === '\u0000') {
26033                     break;
26034                 }
26035                 str += c;
26036             }
26037             return str;
26038         }
26039         return values;
26040     }
26041     
26042 });
26043
26044 Roo.apply(Roo.bootstrap.UploadCropbox, {
26045     tags : {
26046         'Orientation': 0x0112
26047     },
26048     
26049     Orientation: {
26050             1: 0, //'top-left',
26051 //            2: 'top-right',
26052             3: 180, //'bottom-right',
26053 //            4: 'bottom-left',
26054 //            5: 'left-top',
26055             6: 90, //'right-top',
26056 //            7: 'right-bottom',
26057             8: 270 //'left-bottom'
26058     },
26059     
26060     exifTagTypes : {
26061         // byte, 8-bit unsigned int:
26062         1: {
26063             getValue: function (dataView, dataOffset) {
26064                 return dataView.getUint8(dataOffset);
26065             },
26066             size: 1
26067         },
26068         // ascii, 8-bit byte:
26069         2: {
26070             getValue: function (dataView, dataOffset) {
26071                 return String.fromCharCode(dataView.getUint8(dataOffset));
26072             },
26073             size: 1,
26074             ascii: true
26075         },
26076         // short, 16 bit int:
26077         3: {
26078             getValue: function (dataView, dataOffset, littleEndian) {
26079                 return dataView.getUint16(dataOffset, littleEndian);
26080             },
26081             size: 2
26082         },
26083         // long, 32 bit int:
26084         4: {
26085             getValue: function (dataView, dataOffset, littleEndian) {
26086                 return dataView.getUint32(dataOffset, littleEndian);
26087             },
26088             size: 4
26089         },
26090         // rational = two long values, first is numerator, second is denominator:
26091         5: {
26092             getValue: function (dataView, dataOffset, littleEndian) {
26093                 return dataView.getUint32(dataOffset, littleEndian) /
26094                     dataView.getUint32(dataOffset + 4, littleEndian);
26095             },
26096             size: 8
26097         },
26098         // slong, 32 bit signed int:
26099         9: {
26100             getValue: function (dataView, dataOffset, littleEndian) {
26101                 return dataView.getInt32(dataOffset, littleEndian);
26102             },
26103             size: 4
26104         },
26105         // srational, two slongs, first is numerator, second is denominator:
26106         10: {
26107             getValue: function (dataView, dataOffset, littleEndian) {
26108                 return dataView.getInt32(dataOffset, littleEndian) /
26109                     dataView.getInt32(dataOffset + 4, littleEndian);
26110             },
26111             size: 8
26112         }
26113     },
26114     
26115     footer : {
26116         STANDARD : [
26117             {
26118                 tag : 'div',
26119                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26120                 action : 'rotate-left',
26121                 cn : [
26122                     {
26123                         tag : 'button',
26124                         cls : 'btn btn-default',
26125                         html : '<i class="fa fa-undo"></i>'
26126                     }
26127                 ]
26128             },
26129             {
26130                 tag : 'div',
26131                 cls : 'btn-group roo-upload-cropbox-picture',
26132                 action : 'picture',
26133                 cn : [
26134                     {
26135                         tag : 'button',
26136                         cls : 'btn btn-default',
26137                         html : '<i class="fa fa-picture-o"></i>'
26138                     }
26139                 ]
26140             },
26141             {
26142                 tag : 'div',
26143                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26144                 action : 'rotate-right',
26145                 cn : [
26146                     {
26147                         tag : 'button',
26148                         cls : 'btn btn-default',
26149                         html : '<i class="fa fa-repeat"></i>'
26150                     }
26151                 ]
26152             }
26153         ],
26154         DOCUMENT : [
26155             {
26156                 tag : 'div',
26157                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26158                 action : 'rotate-left',
26159                 cn : [
26160                     {
26161                         tag : 'button',
26162                         cls : 'btn btn-default',
26163                         html : '<i class="fa fa-undo"></i>'
26164                     }
26165                 ]
26166             },
26167             {
26168                 tag : 'div',
26169                 cls : 'btn-group roo-upload-cropbox-download',
26170                 action : 'download',
26171                 cn : [
26172                     {
26173                         tag : 'button',
26174                         cls : 'btn btn-default',
26175                         html : '<i class="fa fa-download"></i>'
26176                     }
26177                 ]
26178             },
26179             {
26180                 tag : 'div',
26181                 cls : 'btn-group roo-upload-cropbox-crop',
26182                 action : 'crop',
26183                 cn : [
26184                     {
26185                         tag : 'button',
26186                         cls : 'btn btn-default',
26187                         html : '<i class="fa fa-crop"></i>'
26188                     }
26189                 ]
26190             },
26191             {
26192                 tag : 'div',
26193                 cls : 'btn-group roo-upload-cropbox-trash',
26194                 action : 'trash',
26195                 cn : [
26196                     {
26197                         tag : 'button',
26198                         cls : 'btn btn-default',
26199                         html : '<i class="fa fa-trash"></i>'
26200                     }
26201                 ]
26202             },
26203             {
26204                 tag : 'div',
26205                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26206                 action : 'rotate-right',
26207                 cn : [
26208                     {
26209                         tag : 'button',
26210                         cls : 'btn btn-default',
26211                         html : '<i class="fa fa-repeat"></i>'
26212                     }
26213                 ]
26214             }
26215         ],
26216         ROTATOR : [
26217             {
26218                 tag : 'div',
26219                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26220                 action : 'rotate-left',
26221                 cn : [
26222                     {
26223                         tag : 'button',
26224                         cls : 'btn btn-default',
26225                         html : '<i class="fa fa-undo"></i>'
26226                     }
26227                 ]
26228             },
26229             {
26230                 tag : 'div',
26231                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26232                 action : 'rotate-right',
26233                 cn : [
26234                     {
26235                         tag : 'button',
26236                         cls : 'btn btn-default',
26237                         html : '<i class="fa fa-repeat"></i>'
26238                     }
26239                 ]
26240             }
26241         ]
26242     }
26243 });
26244
26245 /*
26246 * Licence: LGPL
26247 */
26248
26249 /**
26250  * @class Roo.bootstrap.DocumentManager
26251  * @extends Roo.bootstrap.Component
26252  * Bootstrap DocumentManager class
26253  * @cfg {String} paramName default 'imageUpload'
26254  * @cfg {String} method default POST
26255  * @cfg {String} url action url
26256  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26257  * @cfg {Boolean} multiple multiple upload default true
26258  * @cfg {Number} thumbSize default 300
26259  * @cfg {String} fieldLabel
26260  * @cfg {Number} labelWidth default 4
26261  * @cfg {String} labelAlign (left|top) default left
26262  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26263  * 
26264  * @constructor
26265  * Create a new DocumentManager
26266  * @param {Object} config The config object
26267  */
26268
26269 Roo.bootstrap.DocumentManager = function(config){
26270     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26271     
26272     this.addEvents({
26273         /**
26274          * @event initial
26275          * Fire when initial the DocumentManager
26276          * @param {Roo.bootstrap.DocumentManager} this
26277          */
26278         "initial" : true,
26279         /**
26280          * @event inspect
26281          * inspect selected file
26282          * @param {Roo.bootstrap.DocumentManager} this
26283          * @param {File} file
26284          */
26285         "inspect" : true,
26286         /**
26287          * @event exception
26288          * Fire when xhr load exception
26289          * @param {Roo.bootstrap.DocumentManager} this
26290          * @param {XMLHttpRequest} xhr
26291          */
26292         "exception" : true,
26293         /**
26294          * @event prepare
26295          * prepare the form data
26296          * @param {Roo.bootstrap.DocumentManager} this
26297          * @param {Object} formData
26298          */
26299         "prepare" : true,
26300         /**
26301          * @event remove
26302          * Fire when remove the file
26303          * @param {Roo.bootstrap.DocumentManager} this
26304          * @param {Object} file
26305          */
26306         "remove" : true,
26307         /**
26308          * @event refresh
26309          * Fire after refresh the file
26310          * @param {Roo.bootstrap.DocumentManager} this
26311          */
26312         "refresh" : true,
26313         /**
26314          * @event click
26315          * Fire after click the image
26316          * @param {Roo.bootstrap.DocumentManager} this
26317          * @param {Object} file
26318          */
26319         "click" : true,
26320         /**
26321          * @event edit
26322          * Fire when upload a image and editable set to true
26323          * @param {Roo.bootstrap.DocumentManager} this
26324          * @param {Object} file
26325          */
26326         "edit" : true,
26327         /**
26328          * @event beforeselectfile
26329          * Fire before select file
26330          * @param {Roo.bootstrap.DocumentManager} this
26331          */
26332         "beforeselectfile" : true,
26333         /**
26334          * @event process
26335          * Fire before process file
26336          * @param {Roo.bootstrap.DocumentManager} this
26337          * @param {Object} file
26338          */
26339         "process" : true
26340         
26341     });
26342 };
26343
26344 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26345     
26346     boxes : 0,
26347     inputName : '',
26348     thumbSize : 300,
26349     multiple : true,
26350     files : [],
26351     method : 'POST',
26352     url : '',
26353     paramName : 'imageUpload',
26354     fieldLabel : '',
26355     labelWidth : 4,
26356     labelAlign : 'left',
26357     editable : true,
26358     delegates : [],
26359     
26360     
26361     xhr : false, 
26362     
26363     getAutoCreate : function()
26364     {   
26365         var managerWidget = {
26366             tag : 'div',
26367             cls : 'roo-document-manager',
26368             cn : [
26369                 {
26370                     tag : 'input',
26371                     cls : 'roo-document-manager-selector',
26372                     type : 'file'
26373                 },
26374                 {
26375                     tag : 'div',
26376                     cls : 'roo-document-manager-uploader',
26377                     cn : [
26378                         {
26379                             tag : 'div',
26380                             cls : 'roo-document-manager-upload-btn',
26381                             html : '<i class="fa fa-plus"></i>'
26382                         }
26383                     ]
26384                     
26385                 }
26386             ]
26387         };
26388         
26389         var content = [
26390             {
26391                 tag : 'div',
26392                 cls : 'column col-md-12',
26393                 cn : managerWidget
26394             }
26395         ];
26396         
26397         if(this.fieldLabel.length){
26398             
26399             content = [
26400                 {
26401                     tag : 'div',
26402                     cls : 'column col-md-12',
26403                     html : this.fieldLabel
26404                 },
26405                 {
26406                     tag : 'div',
26407                     cls : 'column col-md-12',
26408                     cn : managerWidget
26409                 }
26410             ];
26411
26412             if(this.labelAlign == 'left'){
26413                 content = [
26414                     {
26415                         tag : 'div',
26416                         cls : 'column col-md-' + this.labelWidth,
26417                         html : this.fieldLabel
26418                     },
26419                     {
26420                         tag : 'div',
26421                         cls : 'column col-md-' + (12 - this.labelWidth),
26422                         cn : managerWidget
26423                     }
26424                 ];
26425                 
26426             }
26427         }
26428         
26429         var cfg = {
26430             tag : 'div',
26431             cls : 'row clearfix',
26432             cn : content
26433         };
26434         
26435         return cfg;
26436         
26437     },
26438     
26439     initEvents : function()
26440     {
26441         this.managerEl = this.el.select('.roo-document-manager', true).first();
26442         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26443         
26444         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26445         this.selectorEl.hide();
26446         
26447         if(this.multiple){
26448             this.selectorEl.attr('multiple', 'multiple');
26449         }
26450         
26451         this.selectorEl.on('change', this.onFileSelected, this);
26452         
26453         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26454         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26455         
26456         this.uploader.on('click', this.onUploaderClick, this);
26457         
26458         this.renderProgressDialog();
26459         
26460         var _this = this;
26461         
26462         window.addEventListener("resize", function() { _this.refresh(); } );
26463         
26464         this.fireEvent('initial', this);
26465     },
26466     
26467     renderProgressDialog : function()
26468     {
26469         var _this = this;
26470         
26471         this.progressDialog = new Roo.bootstrap.Modal({
26472             cls : 'roo-document-manager-progress-dialog',
26473             allow_close : false,
26474             title : '',
26475             buttons : [
26476                 {
26477                     name  :'cancel',
26478                     weight : 'danger',
26479                     html : 'Cancel'
26480                 }
26481             ], 
26482             listeners : { 
26483                 btnclick : function() {
26484                     _this.uploadCancel();
26485                     this.hide();
26486                 }
26487             }
26488         });
26489          
26490         this.progressDialog.render(Roo.get(document.body));
26491          
26492         this.progress = new Roo.bootstrap.Progress({
26493             cls : 'roo-document-manager-progress',
26494             active : true,
26495             striped : true
26496         });
26497         
26498         this.progress.render(this.progressDialog.getChildContainer());
26499         
26500         this.progressBar = new Roo.bootstrap.ProgressBar({
26501             cls : 'roo-document-manager-progress-bar',
26502             aria_valuenow : 0,
26503             aria_valuemin : 0,
26504             aria_valuemax : 12,
26505             panel : 'success'
26506         });
26507         
26508         this.progressBar.render(this.progress.getChildContainer());
26509     },
26510     
26511     onUploaderClick : function(e)
26512     {
26513         e.preventDefault();
26514      
26515         if(this.fireEvent('beforeselectfile', this) != false){
26516             this.selectorEl.dom.click();
26517         }
26518         
26519     },
26520     
26521     onFileSelected : function(e)
26522     {
26523         e.preventDefault();
26524         
26525         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26526             return;
26527         }
26528         
26529         Roo.each(this.selectorEl.dom.files, function(file){
26530             if(this.fireEvent('inspect', this, file) != false){
26531                 this.files.push(file);
26532             }
26533         }, this);
26534         
26535         this.queue();
26536         
26537     },
26538     
26539     queue : function()
26540     {
26541         this.selectorEl.dom.value = '';
26542         
26543         if(!this.files.length){
26544             return;
26545         }
26546         
26547         if(this.boxes > 0 && this.files.length > this.boxes){
26548             this.files = this.files.slice(0, this.boxes);
26549         }
26550         
26551         this.uploader.show();
26552         
26553         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26554             this.uploader.hide();
26555         }
26556         
26557         var _this = this;
26558         
26559         var files = [];
26560         
26561         var docs = [];
26562         
26563         Roo.each(this.files, function(file){
26564             
26565             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26566                 var f = this.renderPreview(file);
26567                 files.push(f);
26568                 return;
26569             }
26570             
26571             if(file.type.indexOf('image') != -1){
26572                 this.delegates.push(
26573                     (function(){
26574                         _this.process(file);
26575                     }).createDelegate(this)
26576                 );
26577         
26578                 return;
26579             }
26580             
26581             docs.push(
26582                 (function(){
26583                     _this.process(file);
26584                 }).createDelegate(this)
26585             );
26586             
26587         }, this);
26588         
26589         this.files = files;
26590         
26591         this.delegates = this.delegates.concat(docs);
26592         
26593         if(!this.delegates.length){
26594             this.refresh();
26595             return;
26596         }
26597         
26598         this.progressBar.aria_valuemax = this.delegates.length;
26599         
26600         this.arrange();
26601         
26602         return;
26603     },
26604     
26605     arrange : function()
26606     {
26607         if(!this.delegates.length){
26608             this.progressDialog.hide();
26609             this.refresh();
26610             return;
26611         }
26612         
26613         var delegate = this.delegates.shift();
26614         
26615         this.progressDialog.show();
26616         
26617         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26618         
26619         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26620         
26621         delegate();
26622     },
26623     
26624     refresh : function()
26625     {
26626         this.uploader.show();
26627         
26628         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26629             this.uploader.hide();
26630         }
26631         
26632         Roo.isTouch ? this.closable(false) : this.closable(true);
26633         
26634         this.fireEvent('refresh', this);
26635     },
26636     
26637     onRemove : function(e, el, o)
26638     {
26639         e.preventDefault();
26640         
26641         this.fireEvent('remove', this, o);
26642         
26643     },
26644     
26645     remove : function(o)
26646     {
26647         var files = [];
26648         
26649         Roo.each(this.files, function(file){
26650             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26651                 files.push(file);
26652                 return;
26653             }
26654
26655             o.target.remove();
26656
26657         }, this);
26658         
26659         this.files = files;
26660         
26661         this.refresh();
26662     },
26663     
26664     clear : function()
26665     {
26666         Roo.each(this.files, function(file){
26667             if(!file.target){
26668                 return;
26669             }
26670             
26671             file.target.remove();
26672
26673         }, this);
26674         
26675         this.files = [];
26676         
26677         this.refresh();
26678     },
26679     
26680     onClick : function(e, el, o)
26681     {
26682         e.preventDefault();
26683         
26684         this.fireEvent('click', this, o);
26685         
26686     },
26687     
26688     closable : function(closable)
26689     {
26690         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26691             
26692             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26693             
26694             if(closable){
26695                 el.show();
26696                 return;
26697             }
26698             
26699             el.hide();
26700             
26701         }, this);
26702     },
26703     
26704     xhrOnLoad : function(xhr)
26705     {
26706         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26707             el.remove();
26708         }, this);
26709         
26710         if (xhr.readyState !== 4) {
26711             this.arrange();
26712             this.fireEvent('exception', this, xhr);
26713             return;
26714         }
26715
26716         var response = Roo.decode(xhr.responseText);
26717         
26718         if(!response.success){
26719             this.arrange();
26720             this.fireEvent('exception', this, xhr);
26721             return;
26722         }
26723         
26724         var file = this.renderPreview(response.data);
26725         
26726         this.files.push(file);
26727         
26728         this.arrange();
26729         
26730     },
26731     
26732     xhrOnError : function()
26733     {
26734         Roo.log('xhr on error');
26735         
26736         var response = Roo.decode(xhr.responseText);
26737           
26738         Roo.log(response);
26739         
26740         this.arrange();
26741     },
26742     
26743     process : function(file)
26744     {
26745         if(this.fireEvent('process', this, file) !== false){
26746             if(this.editable && file.type.indexOf('image') != -1){
26747                 this.fireEvent('edit', this, file);
26748                 return;
26749             }
26750
26751             this.uploadStart(file, false);
26752
26753             return;
26754         }
26755         
26756     },
26757     
26758     uploadStart : function(file, crop)
26759     {
26760         this.xhr = new XMLHttpRequest();
26761         
26762         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26763             this.arrange();
26764             return;
26765         }
26766         
26767         file.xhr = this.xhr;
26768             
26769         this.managerEl.createChild({
26770             tag : 'div',
26771             cls : 'roo-document-manager-loading',
26772             cn : [
26773                 {
26774                     tag : 'div',
26775                     tooltip : file.name,
26776                     cls : 'roo-document-manager-thumb',
26777                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26778                 }
26779             ]
26780
26781         });
26782
26783         this.xhr.open(this.method, this.url, true);
26784         
26785         var headers = {
26786             "Accept": "application/json",
26787             "Cache-Control": "no-cache",
26788             "X-Requested-With": "XMLHttpRequest"
26789         };
26790         
26791         for (var headerName in headers) {
26792             var headerValue = headers[headerName];
26793             if (headerValue) {
26794                 this.xhr.setRequestHeader(headerName, headerValue);
26795             }
26796         }
26797         
26798         var _this = this;
26799         
26800         this.xhr.onload = function()
26801         {
26802             _this.xhrOnLoad(_this.xhr);
26803         }
26804         
26805         this.xhr.onerror = function()
26806         {
26807             _this.xhrOnError(_this.xhr);
26808         }
26809         
26810         var formData = new FormData();
26811
26812         formData.append('returnHTML', 'NO');
26813         
26814         if(crop){
26815             formData.append('crop', crop);
26816         }
26817         
26818         formData.append(this.paramName, file, file.name);
26819         
26820         if(this.fireEvent('prepare', this, formData) != false){
26821             this.xhr.send(formData);
26822         };
26823     },
26824     
26825     uploadCancel : function()
26826     {
26827         if (this.xhr) {
26828             this.xhr.abort();
26829         }
26830         
26831         
26832         this.delegates = [];
26833         
26834         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26835             el.remove();
26836         }, this);
26837         
26838         this.arrange();
26839     },
26840     
26841     renderPreview : function(file)
26842     {
26843         if(typeof(file.target) != 'undefined' && file.target){
26844             return file;
26845         }
26846         
26847         var previewEl = this.managerEl.createChild({
26848             tag : 'div',
26849             cls : 'roo-document-manager-preview',
26850             cn : [
26851                 {
26852                     tag : 'div',
26853                     tooltip : file.filename,
26854                     cls : 'roo-document-manager-thumb',
26855                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26856                 },
26857                 {
26858                     tag : 'button',
26859                     cls : 'close',
26860                     html : '<i class="fa fa-times-circle"></i>'
26861                 }
26862             ]
26863         });
26864
26865         var close = previewEl.select('button.close', true).first();
26866
26867         close.on('click', this.onRemove, this, file);
26868
26869         file.target = previewEl;
26870
26871         var image = previewEl.select('img', true).first();
26872         
26873         var _this = this;
26874         
26875         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26876         
26877         image.on('click', this.onClick, this, file);
26878         
26879         return file;
26880         
26881     },
26882     
26883     onPreviewLoad : function(file, image)
26884     {
26885         if(typeof(file.target) == 'undefined' || !file.target){
26886             return;
26887         }
26888         
26889         var width = image.dom.naturalWidth || image.dom.width;
26890         var height = image.dom.naturalHeight || image.dom.height;
26891         
26892         if(width > height){
26893             file.target.addClass('wide');
26894             return;
26895         }
26896         
26897         file.target.addClass('tall');
26898         return;
26899         
26900     },
26901     
26902     uploadFromSource : function(file, crop)
26903     {
26904         this.xhr = new XMLHttpRequest();
26905         
26906         this.managerEl.createChild({
26907             tag : 'div',
26908             cls : 'roo-document-manager-loading',
26909             cn : [
26910                 {
26911                     tag : 'div',
26912                     tooltip : file.name,
26913                     cls : 'roo-document-manager-thumb',
26914                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26915                 }
26916             ]
26917
26918         });
26919
26920         this.xhr.open(this.method, this.url, true);
26921         
26922         var headers = {
26923             "Accept": "application/json",
26924             "Cache-Control": "no-cache",
26925             "X-Requested-With": "XMLHttpRequest"
26926         };
26927         
26928         for (var headerName in headers) {
26929             var headerValue = headers[headerName];
26930             if (headerValue) {
26931                 this.xhr.setRequestHeader(headerName, headerValue);
26932             }
26933         }
26934         
26935         var _this = this;
26936         
26937         this.xhr.onload = function()
26938         {
26939             _this.xhrOnLoad(_this.xhr);
26940         }
26941         
26942         this.xhr.onerror = function()
26943         {
26944             _this.xhrOnError(_this.xhr);
26945         }
26946         
26947         var formData = new FormData();
26948
26949         formData.append('returnHTML', 'NO');
26950         
26951         formData.append('crop', crop);
26952         
26953         if(typeof(file.filename) != 'undefined'){
26954             formData.append('filename', file.filename);
26955         }
26956         
26957         if(typeof(file.mimetype) != 'undefined'){
26958             formData.append('mimetype', file.mimetype);
26959         }
26960         
26961         if(this.fireEvent('prepare', this, formData) != false){
26962             this.xhr.send(formData);
26963         };
26964     }
26965 });
26966
26967 /*
26968 * Licence: LGPL
26969 */
26970
26971 /**
26972  * @class Roo.bootstrap.DocumentViewer
26973  * @extends Roo.bootstrap.Component
26974  * Bootstrap DocumentViewer class
26975  * 
26976  * @constructor
26977  * Create a new DocumentViewer
26978  * @param {Object} config The config object
26979  */
26980
26981 Roo.bootstrap.DocumentViewer = function(config){
26982     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26983     
26984     this.addEvents({
26985         /**
26986          * @event initial
26987          * Fire after initEvent
26988          * @param {Roo.bootstrap.DocumentViewer} this
26989          */
26990         "initial" : true,
26991         /**
26992          * @event click
26993          * Fire after click
26994          * @param {Roo.bootstrap.DocumentViewer} this
26995          */
26996         "click" : true,
26997         /**
26998          * @event trash
26999          * Fire after trash button
27000          * @param {Roo.bootstrap.DocumentViewer} this
27001          */
27002         "trash" : true
27003         
27004     });
27005 };
27006
27007 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27008     
27009     getAutoCreate : function()
27010     {
27011         var cfg = {
27012             tag : 'div',
27013             cls : 'roo-document-viewer',
27014             cn : [
27015                 {
27016                     tag : 'div',
27017                     cls : 'roo-document-viewer-body',
27018                     cn : [
27019                         {
27020                             tag : 'div',
27021                             cls : 'roo-document-viewer-thumb',
27022                             cn : [
27023                                 {
27024                                     tag : 'img',
27025                                     cls : 'roo-document-viewer-image'
27026                                 }
27027                             ]
27028                         }
27029                     ]
27030                 },
27031                 {
27032                     tag : 'div',
27033                     cls : 'roo-document-viewer-footer',
27034                     cn : {
27035                         tag : 'div',
27036                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27037                         cn : [
27038                             {
27039                                 tag : 'div',
27040                                 cls : 'btn-group',
27041                                 cn : [
27042                                     {
27043                                         tag : 'button',
27044                                         cls : 'btn btn-default roo-document-viewer-trash',
27045                                         html : '<i class="fa fa-trash"></i>'
27046                                     }
27047                                 ]
27048                             }
27049                         ]
27050                     }
27051                 }
27052             ]
27053         };
27054         
27055         return cfg;
27056     },
27057     
27058     initEvents : function()
27059     {
27060         
27061         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27062         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27063         
27064         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27065         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27066         
27067         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27068         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069         
27070         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27071         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072         
27073         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27074         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27075         
27076         this.bodyEl.on('click', this.onClick, this);
27077         
27078         this.trashBtn.on('click', this.onTrash, this);
27079         
27080     },
27081     
27082     initial : function()
27083     {
27084 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27085         
27086         
27087         this.fireEvent('initial', this);
27088         
27089     },
27090     
27091     onClick : function(e)
27092     {
27093         e.preventDefault();
27094         
27095         this.fireEvent('click', this);
27096     },
27097     
27098     onTrash : function(e)
27099     {
27100         e.preventDefault();
27101         
27102         this.fireEvent('trash', this);
27103     }
27104     
27105 });
27106 /*
27107  * - LGPL
27108  *
27109  * nav progress bar
27110  * 
27111  */
27112
27113 /**
27114  * @class Roo.bootstrap.NavProgressBar
27115  * @extends Roo.bootstrap.Component
27116  * Bootstrap NavProgressBar class
27117  * 
27118  * @constructor
27119  * Create a new nav progress bar
27120  * @param {Object} config The config object
27121  */
27122
27123 Roo.bootstrap.NavProgressBar = function(config){
27124     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27125
27126     this.bullets = this.bullets || [];
27127    
27128 //    Roo.bootstrap.NavProgressBar.register(this);
27129      this.addEvents({
27130         /**
27131              * @event changed
27132              * Fires when the active item changes
27133              * @param {Roo.bootstrap.NavProgressBar} this
27134              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27135              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27136          */
27137         'changed': true
27138      });
27139     
27140 };
27141
27142 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27143     
27144     bullets : [],
27145     barItems : [],
27146     
27147     getAutoCreate : function()
27148     {
27149         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27150         
27151         cfg = {
27152             tag : 'div',
27153             cls : 'roo-navigation-bar-group',
27154             cn : [
27155                 {
27156                     tag : 'div',
27157                     cls : 'roo-navigation-top-bar'
27158                 },
27159                 {
27160                     tag : 'div',
27161                     cls : 'roo-navigation-bullets-bar',
27162                     cn : [
27163                         {
27164                             tag : 'ul',
27165                             cls : 'roo-navigation-bar'
27166                         }
27167                     ]
27168                 },
27169                 
27170                 {
27171                     tag : 'div',
27172                     cls : 'roo-navigation-bottom-bar'
27173                 }
27174             ]
27175             
27176         };
27177         
27178         return cfg;
27179         
27180     },
27181     
27182     initEvents: function() 
27183     {
27184         
27185     },
27186     
27187     onRender : function(ct, position) 
27188     {
27189         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27190         
27191         if(this.bullets.length){
27192             Roo.each(this.bullets, function(b){
27193                this.addItem(b);
27194             }, this);
27195         }
27196         
27197         this.format();
27198         
27199     },
27200     
27201     addItem : function(cfg)
27202     {
27203         var item = new Roo.bootstrap.NavProgressItem(cfg);
27204         
27205         item.parentId = this.id;
27206         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27207         
27208         if(cfg.html){
27209             var top = new Roo.bootstrap.Element({
27210                 tag : 'div',
27211                 cls : 'roo-navigation-bar-text'
27212             });
27213             
27214             var bottom = new Roo.bootstrap.Element({
27215                 tag : 'div',
27216                 cls : 'roo-navigation-bar-text'
27217             });
27218             
27219             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27220             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27221             
27222             var topText = new Roo.bootstrap.Element({
27223                 tag : 'span',
27224                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27225             });
27226             
27227             var bottomText = new Roo.bootstrap.Element({
27228                 tag : 'span',
27229                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27230             });
27231             
27232             topText.onRender(top.el, null);
27233             bottomText.onRender(bottom.el, null);
27234             
27235             item.topEl = top;
27236             item.bottomEl = bottom;
27237         }
27238         
27239         this.barItems.push(item);
27240         
27241         return item;
27242     },
27243     
27244     getActive : function()
27245     {
27246         var active = false;
27247         
27248         Roo.each(this.barItems, function(v){
27249             
27250             if (!v.isActive()) {
27251                 return;
27252             }
27253             
27254             active = v;
27255             return false;
27256             
27257         });
27258         
27259         return active;
27260     },
27261     
27262     setActiveItem : function(item)
27263     {
27264         var prev = false;
27265         
27266         Roo.each(this.barItems, function(v){
27267             if (v.rid == item.rid) {
27268                 return ;
27269             }
27270             
27271             if (v.isActive()) {
27272                 v.setActive(false);
27273                 prev = v;
27274             }
27275         });
27276
27277         item.setActive(true);
27278         
27279         this.fireEvent('changed', this, item, prev);
27280     },
27281     
27282     getBarItem: function(rid)
27283     {
27284         var ret = false;
27285         
27286         Roo.each(this.barItems, function(e) {
27287             if (e.rid != rid) {
27288                 return;
27289             }
27290             
27291             ret =  e;
27292             return false;
27293         });
27294         
27295         return ret;
27296     },
27297     
27298     indexOfItem : function(item)
27299     {
27300         var index = false;
27301         
27302         Roo.each(this.barItems, function(v, i){
27303             
27304             if (v.rid != item.rid) {
27305                 return;
27306             }
27307             
27308             index = i;
27309             return false
27310         });
27311         
27312         return index;
27313     },
27314     
27315     setActiveNext : function()
27316     {
27317         var i = this.indexOfItem(this.getActive());
27318         
27319         if (i > this.barItems.length) {
27320             return;
27321         }
27322         
27323         this.setActiveItem(this.barItems[i+1]);
27324     },
27325     
27326     setActivePrev : function()
27327     {
27328         var i = this.indexOfItem(this.getActive());
27329         
27330         if (i  < 1) {
27331             return;
27332         }
27333         
27334         this.setActiveItem(this.barItems[i-1]);
27335     },
27336     
27337     format : function()
27338     {
27339         if(!this.barItems.length){
27340             return;
27341         }
27342      
27343         var width = 100 / this.barItems.length;
27344         
27345         Roo.each(this.barItems, function(i){
27346             i.el.setStyle('width', width + '%');
27347             i.topEl.el.setStyle('width', width + '%');
27348             i.bottomEl.el.setStyle('width', width + '%');
27349         }, this);
27350         
27351     }
27352     
27353 });
27354 /*
27355  * - LGPL
27356  *
27357  * Nav Progress Item
27358  * 
27359  */
27360
27361 /**
27362  * @class Roo.bootstrap.NavProgressItem
27363  * @extends Roo.bootstrap.Component
27364  * Bootstrap NavProgressItem class
27365  * @cfg {String} rid the reference id
27366  * @cfg {Boolean} active (true|false) Is item active default false
27367  * @cfg {Boolean} disabled (true|false) Is item active default false
27368  * @cfg {String} html
27369  * @cfg {String} position (top|bottom) text position default bottom
27370  * @cfg {String} icon show icon instead of number
27371  * 
27372  * @constructor
27373  * Create a new NavProgressItem
27374  * @param {Object} config The config object
27375  */
27376 Roo.bootstrap.NavProgressItem = function(config){
27377     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27378     this.addEvents({
27379         // raw events
27380         /**
27381          * @event click
27382          * The raw click event for the entire grid.
27383          * @param {Roo.bootstrap.NavProgressItem} this
27384          * @param {Roo.EventObject} e
27385          */
27386         "click" : true
27387     });
27388    
27389 };
27390
27391 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27392     
27393     rid : '',
27394     active : false,
27395     disabled : false,
27396     html : '',
27397     position : 'bottom',
27398     icon : false,
27399     
27400     getAutoCreate : function()
27401     {
27402         var iconCls = 'roo-navigation-bar-item-icon';
27403         
27404         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27405         
27406         var cfg = {
27407             tag: 'li',
27408             cls: 'roo-navigation-bar-item',
27409             cn : [
27410                 {
27411                     tag : 'i',
27412                     cls : iconCls
27413                 }
27414             ]
27415         };
27416         
27417         if(this.active){
27418             cfg.cls += ' active';
27419         }
27420         if(this.disabled){
27421             cfg.cls += ' disabled';
27422         }
27423         
27424         return cfg;
27425     },
27426     
27427     disable : function()
27428     {
27429         this.setDisabled(true);
27430     },
27431     
27432     enable : function()
27433     {
27434         this.setDisabled(false);
27435     },
27436     
27437     initEvents: function() 
27438     {
27439         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27440         
27441         this.iconEl.on('click', this.onClick, this);
27442     },
27443     
27444     onClick : function(e)
27445     {
27446         e.preventDefault();
27447         
27448         if(this.disabled){
27449             return;
27450         }
27451         
27452         if(this.fireEvent('click', this, e) === false){
27453             return;
27454         };
27455         
27456         this.parent().setActiveItem(this);
27457     },
27458     
27459     isActive: function () 
27460     {
27461         return this.active;
27462     },
27463     
27464     setActive : function(state)
27465     {
27466         if(this.active == state){
27467             return;
27468         }
27469         
27470         this.active = state;
27471         
27472         if (state) {
27473             this.el.addClass('active');
27474             return;
27475         }
27476         
27477         this.el.removeClass('active');
27478         
27479         return;
27480     },
27481     
27482     setDisabled : function(state)
27483     {
27484         if(this.disabled == state){
27485             return;
27486         }
27487         
27488         this.disabled = state;
27489         
27490         if (state) {
27491             this.el.addClass('disabled');
27492             return;
27493         }
27494         
27495         this.el.removeClass('disabled');
27496     },
27497     
27498     tooltipEl : function()
27499     {
27500         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27501     }
27502 });
27503  
27504
27505  /*
27506  * - LGPL
27507  *
27508  * FieldLabel
27509  * 
27510  */
27511
27512 /**
27513  * @class Roo.bootstrap.FieldLabel
27514  * @extends Roo.bootstrap.Component
27515  * Bootstrap FieldLabel class
27516  * @cfg {String} html contents of the element
27517  * @cfg {String} tag tag of the element default label
27518  * @cfg {String} cls class of the element
27519  * @cfg {String} target label target 
27520  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27521  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27522  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27523  * @cfg {String} iconTooltip default "This field is required"
27524  * 
27525  * @constructor
27526  * Create a new FieldLabel
27527  * @param {Object} config The config object
27528  */
27529
27530 Roo.bootstrap.FieldLabel = function(config){
27531     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27532     
27533     this.addEvents({
27534             /**
27535              * @event invalid
27536              * Fires after the field has been marked as invalid.
27537              * @param {Roo.form.FieldLabel} this
27538              * @param {String} msg The validation message
27539              */
27540             invalid : true,
27541             /**
27542              * @event valid
27543              * Fires after the field has been validated with no errors.
27544              * @param {Roo.form.FieldLabel} this
27545              */
27546             valid : true
27547         });
27548 };
27549
27550 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27551     
27552     tag: 'label',
27553     cls: '',
27554     html: '',
27555     target: '',
27556     allowBlank : true,
27557     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27558     validClass : 'text-success fa fa-lg fa-check',
27559     iconTooltip : 'This field is required',
27560     
27561     getAutoCreate : function(){
27562         
27563         var cfg = {
27564             tag : this.tag,
27565             cls : 'roo-bootstrap-field-label ' + this.cls,
27566             for : this.target,
27567             cn : [
27568                 {
27569                     tag : 'i',
27570                     cls : '',
27571                     tooltip : this.iconTooltip
27572                 },
27573                 {
27574                     tag : 'span',
27575                     html : this.html
27576                 }
27577             ] 
27578         };
27579         
27580         return cfg;
27581     },
27582     
27583     initEvents: function() 
27584     {
27585         Roo.bootstrap.Element.superclass.initEvents.call(this);
27586         
27587         this.iconEl = this.el.select('i', true).first();
27588         
27589         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27590         
27591         Roo.bootstrap.FieldLabel.register(this);
27592     },
27593     
27594     /**
27595      * Mark this field as valid
27596      */
27597     markValid : function()
27598     {
27599         this.iconEl.show();
27600         
27601         this.iconEl.removeClass(this.invalidClass);
27602         
27603         this.iconEl.addClass(this.validClass);
27604         
27605         this.fireEvent('valid', this);
27606     },
27607     
27608     /**
27609      * Mark this field as invalid
27610      * @param {String} msg The validation message
27611      */
27612     markInvalid : function(msg)
27613     {
27614         this.iconEl.show();
27615         
27616         this.iconEl.removeClass(this.validClass);
27617         
27618         this.iconEl.addClass(this.invalidClass);
27619         
27620         this.fireEvent('invalid', this, msg);
27621     }
27622     
27623    
27624 });
27625
27626 Roo.apply(Roo.bootstrap.FieldLabel, {
27627     
27628     groups: {},
27629     
27630      /**
27631     * register a FieldLabel Group
27632     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27633     */
27634     register : function(label)
27635     {
27636         if(this.groups.hasOwnProperty(label.target)){
27637             return;
27638         }
27639      
27640         this.groups[label.target] = label;
27641         
27642     },
27643     /**
27644     * fetch a FieldLabel Group based on the target
27645     * @param {string} target
27646     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27647     */
27648     get: function(target) {
27649         if (typeof(this.groups[target]) == 'undefined') {
27650             return false;
27651         }
27652         
27653         return this.groups[target] ;
27654     }
27655 });
27656
27657  
27658
27659  /*
27660  * - LGPL
27661  *
27662  * page DateSplitField.
27663  * 
27664  */
27665
27666
27667 /**
27668  * @class Roo.bootstrap.DateSplitField
27669  * @extends Roo.bootstrap.Component
27670  * Bootstrap DateSplitField class
27671  * @cfg {string} fieldLabel - the label associated
27672  * @cfg {Number} labelWidth set the width of label (0-12)
27673  * @cfg {String} labelAlign (top|left)
27674  * @cfg {Boolean} dayAllowBlank (true|false) default false
27675  * @cfg {Boolean} monthAllowBlank (true|false) default false
27676  * @cfg {Boolean} yearAllowBlank (true|false) default false
27677  * @cfg {string} dayPlaceholder 
27678  * @cfg {string} monthPlaceholder
27679  * @cfg {string} yearPlaceholder
27680  * @cfg {string} dayFormat default 'd'
27681  * @cfg {string} monthFormat default 'm'
27682  * @cfg {string} yearFormat default 'Y'
27683
27684  *     
27685  * @constructor
27686  * Create a new DateSplitField
27687  * @param {Object} config The config object
27688  */
27689
27690 Roo.bootstrap.DateSplitField = function(config){
27691     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27692     
27693     this.addEvents({
27694         // raw events
27695          /**
27696          * @event years
27697          * getting the data of years
27698          * @param {Roo.bootstrap.DateSplitField} this
27699          * @param {Object} years
27700          */
27701         "years" : true,
27702         /**
27703          * @event days
27704          * getting the data of days
27705          * @param {Roo.bootstrap.DateSplitField} this
27706          * @param {Object} days
27707          */
27708         "days" : true,
27709         /**
27710          * @event invalid
27711          * Fires after the field has been marked as invalid.
27712          * @param {Roo.form.Field} this
27713          * @param {String} msg The validation message
27714          */
27715         invalid : true,
27716        /**
27717          * @event valid
27718          * Fires after the field has been validated with no errors.
27719          * @param {Roo.form.Field} this
27720          */
27721         valid : true
27722     });
27723 };
27724
27725 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27726     
27727     fieldLabel : '',
27728     labelAlign : 'top',
27729     labelWidth : 3,
27730     dayAllowBlank : false,
27731     monthAllowBlank : false,
27732     yearAllowBlank : false,
27733     dayPlaceholder : '',
27734     monthPlaceholder : '',
27735     yearPlaceholder : '',
27736     dayFormat : 'd',
27737     monthFormat : 'm',
27738     yearFormat : 'Y',
27739     isFormField : true,
27740     
27741     getAutoCreate : function()
27742     {
27743         var cfg = {
27744             tag : 'div',
27745             cls : 'row roo-date-split-field-group',
27746             cn : [
27747                 {
27748                     tag : 'input',
27749                     type : 'hidden',
27750                     cls : 'form-hidden-field roo-date-split-field-group-value',
27751                     name : this.name
27752                 }
27753             ]
27754         };
27755         
27756         if(this.fieldLabel){
27757             cfg.cn.push({
27758                 tag : 'div',
27759                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27760                 cn : [
27761                     {
27762                         tag : 'label',
27763                         html : this.fieldLabel
27764                     }
27765                 ]
27766             });
27767         }
27768         
27769         Roo.each(['day', 'month', 'year'], function(t){
27770             cfg.cn.push({
27771                 tag : 'div',
27772                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27773             });
27774         }, this);
27775         
27776         return cfg;
27777     },
27778     
27779     inputEl: function ()
27780     {
27781         return this.el.select('.roo-date-split-field-group-value', true).first();
27782     },
27783     
27784     onRender : function(ct, position) 
27785     {
27786         var _this = this;
27787         
27788         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27789         
27790         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27791         
27792         this.dayField = new Roo.bootstrap.ComboBox({
27793             allowBlank : this.dayAllowBlank,
27794             alwaysQuery : true,
27795             displayField : 'value',
27796             editable : false,
27797             fieldLabel : '',
27798             forceSelection : true,
27799             mode : 'local',
27800             placeholder : this.dayPlaceholder,
27801             selectOnFocus : true,
27802             tpl : '<div class="select2-result"><b>{value}</b></div>',
27803             triggerAction : 'all',
27804             typeAhead : true,
27805             valueField : 'value',
27806             store : new Roo.data.SimpleStore({
27807                 data : (function() {    
27808                     var days = [];
27809                     _this.fireEvent('days', _this, days);
27810                     return days;
27811                 })(),
27812                 fields : [ 'value' ]
27813             }),
27814             listeners : {
27815                 select : function (_self, record, index)
27816                 {
27817                     _this.setValue(_this.getValue());
27818                 }
27819             }
27820         });
27821
27822         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27823         
27824         this.monthField = new Roo.bootstrap.MonthField({
27825             after : '<i class=\"fa fa-calendar\"></i>',
27826             allowBlank : this.monthAllowBlank,
27827             placeholder : this.monthPlaceholder,
27828             readOnly : true,
27829             listeners : {
27830                 render : function (_self)
27831                 {
27832                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27833                         e.preventDefault();
27834                         _self.focus();
27835                     });
27836                 },
27837                 select : function (_self, oldvalue, newvalue)
27838                 {
27839                     _this.setValue(_this.getValue());
27840                 }
27841             }
27842         });
27843         
27844         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27845         
27846         this.yearField = new Roo.bootstrap.ComboBox({
27847             allowBlank : this.yearAllowBlank,
27848             alwaysQuery : true,
27849             displayField : 'value',
27850             editable : false,
27851             fieldLabel : '',
27852             forceSelection : true,
27853             mode : 'local',
27854             placeholder : this.yearPlaceholder,
27855             selectOnFocus : true,
27856             tpl : '<div class="select2-result"><b>{value}</b></div>',
27857             triggerAction : 'all',
27858             typeAhead : true,
27859             valueField : 'value',
27860             store : new Roo.data.SimpleStore({
27861                 data : (function() {
27862                     var years = [];
27863                     _this.fireEvent('years', _this, years);
27864                     return years;
27865                 })(),
27866                 fields : [ 'value' ]
27867             }),
27868             listeners : {
27869                 select : function (_self, record, index)
27870                 {
27871                     _this.setValue(_this.getValue());
27872                 }
27873             }
27874         });
27875
27876         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27877     },
27878     
27879     setValue : function(v, format)
27880     {
27881         this.inputEl.dom.value = v;
27882         
27883         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27884         
27885         var d = Date.parseDate(v, f);
27886         
27887         if(!d){
27888             this.validate();
27889             return;
27890         }
27891         
27892         this.setDay(d.format(this.dayFormat));
27893         this.setMonth(d.format(this.monthFormat));
27894         this.setYear(d.format(this.yearFormat));
27895         
27896         this.validate();
27897         
27898         return;
27899     },
27900     
27901     setDay : function(v)
27902     {
27903         this.dayField.setValue(v);
27904         this.inputEl.dom.value = this.getValue();
27905         this.validate();
27906         return;
27907     },
27908     
27909     setMonth : function(v)
27910     {
27911         this.monthField.setValue(v, true);
27912         this.inputEl.dom.value = this.getValue();
27913         this.validate();
27914         return;
27915     },
27916     
27917     setYear : function(v)
27918     {
27919         this.yearField.setValue(v);
27920         this.inputEl.dom.value = this.getValue();
27921         this.validate();
27922         return;
27923     },
27924     
27925     getDay : function()
27926     {
27927         return this.dayField.getValue();
27928     },
27929     
27930     getMonth : function()
27931     {
27932         return this.monthField.getValue();
27933     },
27934     
27935     getYear : function()
27936     {
27937         return this.yearField.getValue();
27938     },
27939     
27940     getValue : function()
27941     {
27942         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27943         
27944         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27945         
27946         return date;
27947     },
27948     
27949     reset : function()
27950     {
27951         this.setDay('');
27952         this.setMonth('');
27953         this.setYear('');
27954         this.inputEl.dom.value = '';
27955         this.validate();
27956         return;
27957     },
27958     
27959     validate : function()
27960     {
27961         var d = this.dayField.validate();
27962         var m = this.monthField.validate();
27963         var y = this.yearField.validate();
27964         
27965         var valid = true;
27966         
27967         if(
27968                 (!this.dayAllowBlank && !d) ||
27969                 (!this.monthAllowBlank && !m) ||
27970                 (!this.yearAllowBlank && !y)
27971         ){
27972             valid = false;
27973         }
27974         
27975         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27976             return valid;
27977         }
27978         
27979         if(valid){
27980             this.markValid();
27981             return valid;
27982         }
27983         
27984         this.markInvalid();
27985         
27986         return valid;
27987     },
27988     
27989     markValid : function()
27990     {
27991         
27992         var label = this.el.select('label', true).first();
27993         var icon = this.el.select('i.fa-star', true).first();
27994
27995         if(label && icon){
27996             icon.remove();
27997         }
27998         
27999         this.fireEvent('valid', this);
28000     },
28001     
28002      /**
28003      * Mark this field as invalid
28004      * @param {String} msg The validation message
28005      */
28006     markInvalid : function(msg)
28007     {
28008         
28009         var label = this.el.select('label', true).first();
28010         var icon = this.el.select('i.fa-star', true).first();
28011
28012         if(label && !icon){
28013             this.el.select('.roo-date-split-field-label', true).createChild({
28014                 tag : 'i',
28015                 cls : 'text-danger fa fa-lg fa-star',
28016                 tooltip : 'This field is required',
28017                 style : 'margin-right:5px;'
28018             }, label, true);
28019         }
28020         
28021         this.fireEvent('invalid', this, msg);
28022     },
28023     
28024     clearInvalid : function()
28025     {
28026         var label = this.el.select('label', true).first();
28027         var icon = this.el.select('i.fa-star', true).first();
28028
28029         if(label && icon){
28030             icon.remove();
28031         }
28032         
28033         this.fireEvent('valid', this);
28034     },
28035     
28036     getName: function()
28037     {
28038         return this.name;
28039     }
28040     
28041 });
28042
28043