docs/default.css
[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         
1987         
1988         
1989         if (Roo.isTouch) {
1990             this.el.on('touchstart'  , this.onTouch, this);
1991         }
1992         this.el.on('click' , this.onClick, this);
1993
1994         this.el.on("mouseover", this.onMouseOver, this);
1995         this.el.on("mouseout", this.onMouseOut, this);
1996         
1997         
1998     },
1999     findTargetItem : function(e){
2000         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2001         if(!t){
2002             return false;
2003         }
2004         //Roo.log(t);         Roo.log(t.id);
2005         if(t && t.id){
2006             //Roo.log(this.menuitems);
2007             return this.menuitems.get(t.id);
2008             
2009             //return this.items.get(t.menuItemId);
2010         }
2011         
2012         return false;
2013     },
2014     
2015     onTouch : function(e) {
2016         e.stopEvent();
2017         this.onClick(e);
2018     },
2019     
2020     onClick : function(e){
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){
2602             var _this = this;
2603             (function(){ _this.el.addClass('in'); }).defer(50);
2604         }else{
2605             this.el.addClass('in');
2606         }
2607         
2608         // not sure how we can show data in here.. 
2609         //if (this.tmpl) {
2610         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2611         //}
2612         
2613         Roo.get(document.body).addClass("x-body-masked");
2614         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2615         this.maskEl.show();
2616         this.el.setStyle('zIndex', '10001');
2617        
2618         this.fireEvent('show', this);
2619         
2620         
2621     },
2622     hide : function()
2623     {
2624         this.maskEl.hide();
2625         Roo.get(document.body).removeClass("x-body-masked");
2626         this.el.removeClass('in');
2627         
2628         if(this.animate){
2629             var _this = this;
2630             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2631         }else{
2632             this.el.setStyle('display', 'none');
2633         }
2634         
2635         this.fireEvent('hide', this);
2636     },
2637     
2638     addButton : function(str, cb)
2639     {
2640          
2641         
2642         var b = Roo.apply({}, { html : str } );
2643         b.xns = b.xns || Roo.bootstrap;
2644         b.xtype = b.xtype || 'Button';
2645         if (typeof(b.listeners) == 'undefined') {
2646             b.listeners = { click : cb.createDelegate(this)  };
2647         }
2648         
2649         var btn = Roo.factory(b);
2650            
2651         btn.onRender(this.el.select('.modal-footer div').first());
2652         
2653         return btn;   
2654        
2655     },
2656     
2657     setDefaultButton : function(btn)
2658     {
2659         //this.el.select('.modal-footer').()
2660     },
2661     resizeTo: function(w,h)
2662     {
2663         // skip..
2664     },
2665     setContentSize  : function(w, h)
2666     {
2667         
2668     },
2669     onButtonClick: function(btn,e)
2670     {
2671         //Roo.log([a,b,c]);
2672         this.fireEvent('btnclick', btn.name, e);
2673     },
2674      /**
2675      * Set the title of the Dialog
2676      * @param {String} str new Title
2677      */
2678     setTitle: function(str) {
2679         this.titleEl.dom.innerHTML = str;    
2680     },
2681     /**
2682      * Set the body of the Dialog
2683      * @param {String} str new Title
2684      */
2685     setBody: function(str) {
2686         this.bodyEl.dom.innerHTML = str;    
2687     },
2688     /**
2689      * Set the body of the Dialog using the template
2690      * @param {Obj} data - apply this data to the template and replace the body contents.
2691      */
2692     applyBody: function(obj)
2693     {
2694         if (!this.tmpl) {
2695             Roo.log("Error - using apply Body without a template");
2696             //code
2697         }
2698         this.tmpl.overwrite(this.bodyEl, obj);
2699     }
2700     
2701 });
2702
2703
2704 Roo.apply(Roo.bootstrap.Modal,  {
2705     /**
2706          * Button config that displays a single OK button
2707          * @type Object
2708          */
2709         OK :  [{
2710             name : 'ok',
2711             weight : 'primary',
2712             html : 'OK'
2713         }], 
2714         /**
2715          * Button config that displays Yes and No buttons
2716          * @type Object
2717          */
2718         YESNO : [
2719             {
2720                 name  : 'no',
2721                 html : 'No'
2722             },
2723             {
2724                 name  :'yes',
2725                 weight : 'primary',
2726                 html : 'Yes'
2727             }
2728         ],
2729         
2730         /**
2731          * Button config that displays OK and Cancel buttons
2732          * @type Object
2733          */
2734         OKCANCEL : [
2735             {
2736                name : 'cancel',
2737                 html : 'Cancel'
2738             },
2739             {
2740                 name : 'ok',
2741                 weight : 'primary',
2742                 html : 'OK'
2743             }
2744         ],
2745         /**
2746          * Button config that displays Yes, No and Cancel buttons
2747          * @type Object
2748          */
2749         YESNOCANCEL : [
2750             {
2751                 name : 'yes',
2752                 weight : 'primary',
2753                 html : 'Yes'
2754             },
2755             {
2756                 name : 'no',
2757                 html : 'No'
2758             },
2759             {
2760                 name : 'cancel',
2761                 html : 'Cancel'
2762             }
2763         ]
2764 });
2765  
2766  /*
2767  * - LGPL
2768  *
2769  * messagebox - can be used as a replace
2770  * 
2771  */
2772 /**
2773  * @class Roo.MessageBox
2774  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2775  * Example usage:
2776  *<pre><code>
2777 // Basic alert:
2778 Roo.Msg.alert('Status', 'Changes saved successfully.');
2779
2780 // Prompt for user data:
2781 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2782     if (btn == 'ok'){
2783         // process text value...
2784     }
2785 });
2786
2787 // Show a dialog using config options:
2788 Roo.Msg.show({
2789    title:'Save Changes?',
2790    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2791    buttons: Roo.Msg.YESNOCANCEL,
2792    fn: processResult,
2793    animEl: 'elId'
2794 });
2795 </code></pre>
2796  * @singleton
2797  */
2798 Roo.bootstrap.MessageBox = function(){
2799     var dlg, opt, mask, waitTimer;
2800     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2801     var buttons, activeTextEl, bwidth;
2802
2803     
2804     // private
2805     var handleButton = function(button){
2806         dlg.hide();
2807         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2808     };
2809
2810     // private
2811     var handleHide = function(){
2812         if(opt && opt.cls){
2813             dlg.el.removeClass(opt.cls);
2814         }
2815         //if(waitTimer){
2816         //    Roo.TaskMgr.stop(waitTimer);
2817         //    waitTimer = null;
2818         //}
2819     };
2820
2821     // private
2822     var updateButtons = function(b){
2823         var width = 0;
2824         if(!b){
2825             buttons["ok"].hide();
2826             buttons["cancel"].hide();
2827             buttons["yes"].hide();
2828             buttons["no"].hide();
2829             //dlg.footer.dom.style.display = 'none';
2830             return width;
2831         }
2832         dlg.footerEl.dom.style.display = '';
2833         for(var k in buttons){
2834             if(typeof buttons[k] != "function"){
2835                 if(b[k]){
2836                     buttons[k].show();
2837                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2838                     width += buttons[k].el.getWidth()+15;
2839                 }else{
2840                     buttons[k].hide();
2841                 }
2842             }
2843         }
2844         return width;
2845     };
2846
2847     // private
2848     var handleEsc = function(d, k, e){
2849         if(opt && opt.closable !== false){
2850             dlg.hide();
2851         }
2852         if(e){
2853             e.stopEvent();
2854         }
2855     };
2856
2857     return {
2858         /**
2859          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2860          * @return {Roo.BasicDialog} The BasicDialog element
2861          */
2862         getDialog : function(){
2863            if(!dlg){
2864                 dlg = new Roo.bootstrap.Modal( {
2865                     //draggable: true,
2866                     //resizable:false,
2867                     //constraintoviewport:false,
2868                     //fixedcenter:true,
2869                     //collapsible : false,
2870                     //shim:true,
2871                     //modal: true,
2872                   //  width:400,
2873                   //  height:100,
2874                     //buttonAlign:"center",
2875                     closeClick : function(){
2876                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2877                             handleButton("no");
2878                         }else{
2879                             handleButton("cancel");
2880                         }
2881                     }
2882                 });
2883                 dlg.render();
2884                 dlg.on("hide", handleHide);
2885                 mask = dlg.mask;
2886                 //dlg.addKeyListener(27, handleEsc);
2887                 buttons = {};
2888                 this.buttons = buttons;
2889                 var bt = this.buttonText;
2890                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2891                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2892                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2893                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2894                 //Roo.log(buttons);
2895                 bodyEl = dlg.bodyEl.createChild({
2896
2897                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2898                         '<textarea class="roo-mb-textarea"></textarea>' +
2899                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2900                 });
2901                 msgEl = bodyEl.dom.firstChild;
2902                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2903                 textboxEl.enableDisplayMode();
2904                 textboxEl.addKeyListener([10,13], function(){
2905                     if(dlg.isVisible() && opt && opt.buttons){
2906                         if(opt.buttons.ok){
2907                             handleButton("ok");
2908                         }else if(opt.buttons.yes){
2909                             handleButton("yes");
2910                         }
2911                     }
2912                 });
2913                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2914                 textareaEl.enableDisplayMode();
2915                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2916                 progressEl.enableDisplayMode();
2917                 var pf = progressEl.dom.firstChild;
2918                 if (pf) {
2919                     pp = Roo.get(pf.firstChild);
2920                     pp.setHeight(pf.offsetHeight);
2921                 }
2922                 
2923             }
2924             return dlg;
2925         },
2926
2927         /**
2928          * Updates the message box body text
2929          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2930          * the XHTML-compliant non-breaking space character '&amp;#160;')
2931          * @return {Roo.MessageBox} This message box
2932          */
2933         updateText : function(text){
2934             if(!dlg.isVisible() && !opt.width){
2935                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2936             }
2937             msgEl.innerHTML = text || '&#160;';
2938       
2939             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2940             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2941             var w = Math.max(
2942                     Math.min(opt.width || cw , this.maxWidth), 
2943                     Math.max(opt.minWidth || this.minWidth, bwidth)
2944             );
2945             if(opt.prompt){
2946                 activeTextEl.setWidth(w);
2947             }
2948             if(dlg.isVisible()){
2949                 dlg.fixedcenter = false;
2950             }
2951             // to big, make it scroll. = But as usual stupid IE does not support
2952             // !important..
2953             
2954             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2955                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2956                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2957             } else {
2958                 bodyEl.dom.style.height = '';
2959                 bodyEl.dom.style.overflowY = '';
2960             }
2961             if (cw > w) {
2962                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2963             } else {
2964                 bodyEl.dom.style.overflowX = '';
2965             }
2966             
2967             dlg.setContentSize(w, bodyEl.getHeight());
2968             if(dlg.isVisible()){
2969                 dlg.fixedcenter = true;
2970             }
2971             return this;
2972         },
2973
2974         /**
2975          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2976          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2977          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2978          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2979          * @return {Roo.MessageBox} This message box
2980          */
2981         updateProgress : function(value, text){
2982             if(text){
2983                 this.updateText(text);
2984             }
2985             if (pp) { // weird bug on my firefox - for some reason this is not defined
2986                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2987             }
2988             return this;
2989         },        
2990
2991         /**
2992          * Returns true if the message box is currently displayed
2993          * @return {Boolean} True if the message box is visible, else false
2994          */
2995         isVisible : function(){
2996             return dlg && dlg.isVisible();  
2997         },
2998
2999         /**
3000          * Hides the message box if it is displayed
3001          */
3002         hide : function(){
3003             if(this.isVisible()){
3004                 dlg.hide();
3005             }  
3006         },
3007
3008         /**
3009          * Displays a new message box, or reinitializes an existing message box, based on the config options
3010          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3011          * The following config object properties are supported:
3012          * <pre>
3013 Property    Type             Description
3014 ----------  ---------------  ------------------------------------------------------------------------------------
3015 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3016                                    closes (defaults to undefined)
3017 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3018                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3019 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3020                                    progress and wait dialogs will ignore this property and always hide the
3021                                    close button as they can only be closed programmatically.
3022 cls               String           A custom CSS class to apply to the message box element
3023 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3024                                    displayed (defaults to 75)
3025 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3026                                    function will be btn (the name of the button that was clicked, if applicable,
3027                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3028                                    Progress and wait dialogs will ignore this option since they do not respond to
3029                                    user actions and can only be closed programmatically, so any required function
3030                                    should be called by the same code after it closes the dialog.
3031 icon              String           A CSS class that provides a background image to be used as an icon for
3032                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3033 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3034 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3035 modal             Boolean          False to allow user interaction with the page while the message box is
3036                                    displayed (defaults to true)
3037 msg               String           A string that will replace the existing message box body text (defaults
3038                                    to the XHTML-compliant non-breaking space character '&#160;')
3039 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3040 progress          Boolean          True to display a progress bar (defaults to false)
3041 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3042 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3043 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3044 title             String           The title text
3045 value             String           The string value to set into the active textbox element if displayed
3046 wait              Boolean          True to display a progress bar (defaults to false)
3047 width             Number           The width of the dialog in pixels
3048 </pre>
3049          *
3050          * Example usage:
3051          * <pre><code>
3052 Roo.Msg.show({
3053    title: 'Address',
3054    msg: 'Please enter your address:',
3055    width: 300,
3056    buttons: Roo.MessageBox.OKCANCEL,
3057    multiline: true,
3058    fn: saveAddress,
3059    animEl: 'addAddressBtn'
3060 });
3061 </code></pre>
3062          * @param {Object} config Configuration options
3063          * @return {Roo.MessageBox} This message box
3064          */
3065         show : function(options)
3066         {
3067             
3068             // this causes nightmares if you show one dialog after another
3069             // especially on callbacks..
3070              
3071             if(this.isVisible()){
3072                 
3073                 this.hide();
3074                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3075                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3076                 Roo.log("New Dialog Message:" +  options.msg )
3077                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3078                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3079                 
3080             }
3081             var d = this.getDialog();
3082             opt = options;
3083             d.setTitle(opt.title || "&#160;");
3084             d.closeEl.setDisplayed(opt.closable !== false);
3085             activeTextEl = textboxEl;
3086             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3087             if(opt.prompt){
3088                 if(opt.multiline){
3089                     textboxEl.hide();
3090                     textareaEl.show();
3091                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3092                         opt.multiline : this.defaultTextHeight);
3093                     activeTextEl = textareaEl;
3094                 }else{
3095                     textboxEl.show();
3096                     textareaEl.hide();
3097                 }
3098             }else{
3099                 textboxEl.hide();
3100                 textareaEl.hide();
3101             }
3102             progressEl.setDisplayed(opt.progress === true);
3103             this.updateProgress(0);
3104             activeTextEl.dom.value = opt.value || "";
3105             if(opt.prompt){
3106                 dlg.setDefaultButton(activeTextEl);
3107             }else{
3108                 var bs = opt.buttons;
3109                 var db = null;
3110                 if(bs && bs.ok){
3111                     db = buttons["ok"];
3112                 }else if(bs && bs.yes){
3113                     db = buttons["yes"];
3114                 }
3115                 dlg.setDefaultButton(db);
3116             }
3117             bwidth = updateButtons(opt.buttons);
3118             this.updateText(opt.msg);
3119             if(opt.cls){
3120                 d.el.addClass(opt.cls);
3121             }
3122             d.proxyDrag = opt.proxyDrag === true;
3123             d.modal = opt.modal !== false;
3124             d.mask = opt.modal !== false ? mask : false;
3125             if(!d.isVisible()){
3126                 // force it to the end of the z-index stack so it gets a cursor in FF
3127                 document.body.appendChild(dlg.el.dom);
3128                 d.animateTarget = null;
3129                 d.show(options.animEl);
3130             }
3131             return this;
3132         },
3133
3134         /**
3135          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3136          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3137          * and closing the message box when the process is complete.
3138          * @param {String} title The title bar text
3139          * @param {String} msg The message box body text
3140          * @return {Roo.MessageBox} This message box
3141          */
3142         progress : function(title, msg){
3143             this.show({
3144                 title : title,
3145                 msg : msg,
3146                 buttons: false,
3147                 progress:true,
3148                 closable:false,
3149                 minWidth: this.minProgressWidth,
3150                 modal : true
3151             });
3152             return this;
3153         },
3154
3155         /**
3156          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3157          * If a callback function is passed it will be called after the user clicks the button, and the
3158          * id of the button that was clicked will be passed as the only parameter to the callback
3159          * (could also be the top-right close button).
3160          * @param {String} title The title bar text
3161          * @param {String} msg The message box body text
3162          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3163          * @param {Object} scope (optional) The scope of the callback function
3164          * @return {Roo.MessageBox} This message box
3165          */
3166         alert : function(title, msg, fn, scope){
3167             this.show({
3168                 title : title,
3169                 msg : msg,
3170                 buttons: this.OK,
3171                 fn: fn,
3172                 scope : scope,
3173                 modal : true
3174             });
3175             return this;
3176         },
3177
3178         /**
3179          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3180          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3181          * You are responsible for closing the message box when the process is complete.
3182          * @param {String} msg The message box body text
3183          * @param {String} title (optional) The title bar text
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         wait : function(msg, title){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: false,
3191                 closable:false,
3192                 progress:true,
3193                 modal:true,
3194                 width:300,
3195                 wait:true
3196             });
3197             waitTimer = Roo.TaskMgr.start({
3198                 run: function(i){
3199                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3200                 },
3201                 interval: 1000
3202             });
3203             return this;
3204         },
3205
3206         /**
3207          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3208          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3209          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3210          * @param {String} title The title bar text
3211          * @param {String} msg The message box body text
3212          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3213          * @param {Object} scope (optional) The scope of the callback function
3214          * @return {Roo.MessageBox} This message box
3215          */
3216         confirm : function(title, msg, fn, scope){
3217             this.show({
3218                 title : title,
3219                 msg : msg,
3220                 buttons: this.YESNO,
3221                 fn: fn,
3222                 scope : scope,
3223                 modal : true
3224             });
3225             return this;
3226         },
3227
3228         /**
3229          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3230          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3231          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3232          * (could also be the top-right close button) and the text that was entered will be passed as the two
3233          * parameters to the callback.
3234          * @param {String} title The title bar text
3235          * @param {String} msg The message box body text
3236          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3237          * @param {Object} scope (optional) The scope of the callback function
3238          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3239          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3240          * @return {Roo.MessageBox} This message box
3241          */
3242         prompt : function(title, msg, fn, scope, multiline){
3243             this.show({
3244                 title : title,
3245                 msg : msg,
3246                 buttons: this.OKCANCEL,
3247                 fn: fn,
3248                 minWidth:250,
3249                 scope : scope,
3250                 prompt:true,
3251                 multiline: multiline,
3252                 modal : true
3253             });
3254             return this;
3255         },
3256
3257         /**
3258          * Button config that displays a single OK button
3259          * @type Object
3260          */
3261         OK : {ok:true},
3262         /**
3263          * Button config that displays Yes and No buttons
3264          * @type Object
3265          */
3266         YESNO : {yes:true, no:true},
3267         /**
3268          * Button config that displays OK and Cancel buttons
3269          * @type Object
3270          */
3271         OKCANCEL : {ok:true, cancel:true},
3272         /**
3273          * Button config that displays Yes, No and Cancel buttons
3274          * @type Object
3275          */
3276         YESNOCANCEL : {yes:true, no:true, cancel:true},
3277
3278         /**
3279          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3280          * @type Number
3281          */
3282         defaultTextHeight : 75,
3283         /**
3284          * The maximum width in pixels of the message box (defaults to 600)
3285          * @type Number
3286          */
3287         maxWidth : 600,
3288         /**
3289          * The minimum width in pixels of the message box (defaults to 100)
3290          * @type Number
3291          */
3292         minWidth : 100,
3293         /**
3294          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3295          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3296          * @type Number
3297          */
3298         minProgressWidth : 250,
3299         /**
3300          * An object containing the default button text strings that can be overriden for localized language support.
3301          * Supported properties are: ok, cancel, yes and no.
3302          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3303          * @type Object
3304          */
3305         buttonText : {
3306             ok : "OK",
3307             cancel : "Cancel",
3308             yes : "Yes",
3309             no : "No"
3310         }
3311     };
3312 }();
3313
3314 /**
3315  * Shorthand for {@link Roo.MessageBox}
3316  */
3317 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3318 Roo.Msg = Roo.Msg || Roo.MessageBox;
3319 /*
3320  * - LGPL
3321  *
3322  * navbar
3323  * 
3324  */
3325
3326 /**
3327  * @class Roo.bootstrap.Navbar
3328  * @extends Roo.bootstrap.Component
3329  * Bootstrap Navbar class
3330
3331  * @constructor
3332  * Create a new Navbar
3333  * @param {Object} config The config object
3334  */
3335
3336
3337 Roo.bootstrap.Navbar = function(config){
3338     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3339     
3340 };
3341
3342 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3343     
3344     
3345    
3346     // private
3347     navItems : false,
3348     loadMask : false,
3349     
3350     
3351     getAutoCreate : function(){
3352         
3353         
3354         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3355         
3356     },
3357     
3358     initEvents :function ()
3359     {
3360         //Roo.log(this.el.select('.navbar-toggle',true));
3361         this.el.select('.navbar-toggle',true).on('click', function() {
3362            // Roo.log('click');
3363             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3364         }, this);
3365         
3366         var mark = {
3367             tag: "div",
3368             cls:"x-dlg-mask"
3369         };
3370         
3371         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3372         
3373         var size = this.el.getSize();
3374         this.maskEl.setSize(size.width, size.height);
3375         this.maskEl.enableDisplayMode("block");
3376         this.maskEl.hide();
3377         
3378         if(this.loadMask){
3379             this.maskEl.show();
3380         }
3381     },
3382     
3383     
3384     getChildContainer : function()
3385     {
3386         if (this.el.select('.collapse').getCount()) {
3387             return this.el.select('.collapse',true).first();
3388         }
3389         
3390         return this.el;
3391     },
3392     
3393     mask : function()
3394     {
3395         this.maskEl.show();
3396     },
3397     
3398     unmask : function()
3399     {
3400         this.maskEl.hide();
3401     } 
3402     
3403     
3404     
3405     
3406 });
3407
3408
3409
3410  
3411
3412  /*
3413  * - LGPL
3414  *
3415  * navbar
3416  * 
3417  */
3418
3419 /**
3420  * @class Roo.bootstrap.NavSimplebar
3421  * @extends Roo.bootstrap.Navbar
3422  * Bootstrap Sidebar class
3423  *
3424  * @cfg {Boolean} inverse is inverted color
3425  * 
3426  * @cfg {String} type (nav | pills | tabs)
3427  * @cfg {Boolean} arrangement stacked | justified
3428  * @cfg {String} align (left | right) alignment
3429  * 
3430  * @cfg {Boolean} main (true|false) main nav bar? default false
3431  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3432  * 
3433  * @cfg {String} tag (header|footer|nav|div) default is nav 
3434
3435  * 
3436  * 
3437  * 
3438  * @constructor
3439  * Create a new Sidebar
3440  * @param {Object} config The config object
3441  */
3442
3443
3444 Roo.bootstrap.NavSimplebar = function(config){
3445     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3446 };
3447
3448 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3449     
3450     inverse: false,
3451     
3452     type: false,
3453     arrangement: '',
3454     align : false,
3455     
3456     
3457     
3458     main : false,
3459     
3460     
3461     tag : false,
3462     
3463     
3464     getAutoCreate : function(){
3465         
3466         
3467         var cfg = {
3468             tag : this.tag || 'div',
3469             cls : 'navbar'
3470         };
3471           
3472         
3473         cfg.cn = [
3474             {
3475                 cls: 'nav',
3476                 tag : 'ul'
3477             }
3478         ];
3479         
3480          
3481         this.type = this.type || 'nav';
3482         if (['tabs','pills'].indexOf(this.type)!==-1) {
3483             cfg.cn[0].cls += ' nav-' + this.type
3484         
3485         
3486         } else {
3487             if (this.type!=='nav') {
3488                 Roo.log('nav type must be nav/tabs/pills')
3489             }
3490             cfg.cn[0].cls += ' navbar-nav'
3491         }
3492         
3493         
3494         
3495         
3496         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3497             cfg.cn[0].cls += ' nav-' + this.arrangement;
3498         }
3499         
3500         
3501         if (this.align === 'right') {
3502             cfg.cn[0].cls += ' navbar-right';
3503         }
3504         
3505         if (this.inverse) {
3506             cfg.cls += ' navbar-inverse';
3507             
3508         }
3509         
3510         
3511         return cfg;
3512     
3513         
3514     }
3515     
3516     
3517     
3518 });
3519
3520
3521
3522  
3523
3524  
3525        /*
3526  * - LGPL
3527  *
3528  * navbar
3529  * 
3530  */
3531
3532 /**
3533  * @class Roo.bootstrap.NavHeaderbar
3534  * @extends Roo.bootstrap.NavSimplebar
3535  * Bootstrap Sidebar class
3536  *
3537  * @cfg {String} brand what is brand
3538  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3539  * @cfg {String} brand_href href of the brand
3540  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3541  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3542  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3543  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3544  * 
3545  * @constructor
3546  * Create a new Sidebar
3547  * @param {Object} config The config object
3548  */
3549
3550
3551 Roo.bootstrap.NavHeaderbar = function(config){
3552     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3553       
3554 };
3555
3556 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3557     
3558     position: '',
3559     brand: '',
3560     brand_href: false,
3561     srButton : true,
3562     autohide : false,
3563     desktopCenter : false,
3564    
3565     
3566     getAutoCreate : function(){
3567         
3568         var   cfg = {
3569             tag: this.nav || 'nav',
3570             cls: 'navbar',
3571             role: 'navigation',
3572             cn: []
3573         };
3574         
3575         var cn = cfg.cn;
3576         if (this.desktopCenter) {
3577             cn.push({cls : 'container', cn : []});
3578             cn = cn[0].cn;
3579         }
3580         
3581         if(this.srButton){
3582             cn.push({
3583                 tag: 'div',
3584                 cls: 'navbar-header',
3585                 cn: [
3586                     {
3587                         tag: 'button',
3588                         type: 'button',
3589                         cls: 'navbar-toggle',
3590                         'data-toggle': 'collapse',
3591                         cn: [
3592                             {
3593                                 tag: 'span',
3594                                 cls: 'sr-only',
3595                                 html: 'Toggle navigation'
3596                             },
3597                             {
3598                                 tag: 'span',
3599                                 cls: 'icon-bar'
3600                             },
3601                             {
3602                                 tag: 'span',
3603                                 cls: 'icon-bar'
3604                             },
3605                             {
3606                                 tag: 'span',
3607                                 cls: 'icon-bar'
3608                             }
3609                         ]
3610                     }
3611                 ]
3612             });
3613         }
3614         
3615         cn.push({
3616             tag: 'div',
3617             cls: 'collapse navbar-collapse',
3618             cn : []
3619         });
3620         
3621         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3622         
3623         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3624             cfg.cls += ' navbar-' + this.position;
3625             
3626             // tag can override this..
3627             
3628             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3629         }
3630         
3631         if (this.brand !== '') {
3632             cn[0].cn.push({
3633                 tag: 'a',
3634                 href: this.brand_href ? this.brand_href : '#',
3635                 cls: 'navbar-brand',
3636                 cn: [
3637                 this.brand
3638                 ]
3639             });
3640         }
3641         
3642         if(this.main){
3643             cfg.cls += ' main-nav';
3644         }
3645         
3646         
3647         return cfg;
3648
3649         
3650     },
3651     getHeaderChildContainer : function()
3652     {
3653         if (this.el.select('.navbar-header').getCount()) {
3654             return this.el.select('.navbar-header',true).first();
3655         }
3656         
3657         return this.getChildContainer();
3658     },
3659     
3660     
3661     initEvents : function()
3662     {
3663         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3664         
3665         if (this.autohide) {
3666             
3667             var prevScroll = 0;
3668             var ft = this.el;
3669             
3670             Roo.get(document).on('scroll',function(e) {
3671                 var ns = Roo.get(document).getScroll().top;
3672                 var os = prevScroll;
3673                 prevScroll = ns;
3674                 
3675                 if(ns > os){
3676                     ft.removeClass('slideDown');
3677                     ft.addClass('slideUp');
3678                     return;
3679                 }
3680                 ft.removeClass('slideUp');
3681                 ft.addClass('slideDown');
3682                  
3683               
3684           },this);
3685         }
3686     }    
3687     
3688 });
3689
3690
3691
3692  
3693
3694  /*
3695  * - LGPL
3696  *
3697  * navbar
3698  * 
3699  */
3700
3701 /**
3702  * @class Roo.bootstrap.NavSidebar
3703  * @extends Roo.bootstrap.Navbar
3704  * Bootstrap Sidebar class
3705  * 
3706  * @constructor
3707  * Create a new Sidebar
3708  * @param {Object} config The config object
3709  */
3710
3711
3712 Roo.bootstrap.NavSidebar = function(config){
3713     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3714 };
3715
3716 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3717     
3718     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3719     
3720     getAutoCreate : function(){
3721         
3722         
3723         return  {
3724             tag: 'div',
3725             cls: 'sidebar sidebar-nav'
3726         };
3727     
3728         
3729     }
3730     
3731     
3732     
3733 });
3734
3735
3736
3737  
3738
3739  /*
3740  * - LGPL
3741  *
3742  * nav group
3743  * 
3744  */
3745
3746 /**
3747  * @class Roo.bootstrap.NavGroup
3748  * @extends Roo.bootstrap.Component
3749  * Bootstrap NavGroup class
3750  * @cfg {String} align (left|right)
3751  * @cfg {Boolean} inverse
3752  * @cfg {String} type (nav|pills|tab) default nav
3753  * @cfg {String} navId - reference Id for navbar.
3754
3755  * 
3756  * @constructor
3757  * Create a new nav group
3758  * @param {Object} config The config object
3759  */
3760
3761 Roo.bootstrap.NavGroup = function(config){
3762     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3763     this.navItems = [];
3764    
3765     Roo.bootstrap.NavGroup.register(this);
3766      this.addEvents({
3767         /**
3768              * @event changed
3769              * Fires when the active item changes
3770              * @param {Roo.bootstrap.NavGroup} this
3771              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3772              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3773          */
3774         'changed': true
3775      });
3776     
3777 };
3778
3779 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3780     
3781     align: '',
3782     inverse: false,
3783     form: false,
3784     type: 'nav',
3785     navId : '',
3786     // private
3787     
3788     navItems : false, 
3789     
3790     getAutoCreate : function()
3791     {
3792         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3793         
3794         cfg = {
3795             tag : 'ul',
3796             cls: 'nav' 
3797         };
3798         
3799         if (['tabs','pills'].indexOf(this.type)!==-1) {
3800             cfg.cls += ' nav-' + this.type
3801         } else {
3802             if (this.type!=='nav') {
3803                 Roo.log('nav type must be nav/tabs/pills')
3804             }
3805             cfg.cls += ' navbar-nav'
3806         }
3807         
3808         if (this.parent().sidebar) {
3809             cfg = {
3810                 tag: 'ul',
3811                 cls: 'dashboard-menu sidebar-menu'
3812             };
3813             
3814             return cfg;
3815         }
3816         
3817         if (this.form === true) {
3818             cfg = {
3819                 tag: 'form',
3820                 cls: 'navbar-form'
3821             };
3822             
3823             if (this.align === 'right') {
3824                 cfg.cls += ' navbar-right';
3825             } else {
3826                 cfg.cls += ' navbar-left';
3827             }
3828         }
3829         
3830         if (this.align === 'right') {
3831             cfg.cls += ' navbar-right';
3832         }
3833         
3834         if (this.inverse) {
3835             cfg.cls += ' navbar-inverse';
3836             
3837         }
3838         
3839         
3840         return cfg;
3841     },
3842     /**
3843     * sets the active Navigation item
3844     * @param {Roo.bootstrap.NavItem} the new current navitem
3845     */
3846     setActiveItem : function(item)
3847     {
3848         var prev = false;
3849         Roo.each(this.navItems, function(v){
3850             if (v == item) {
3851                 return ;
3852             }
3853             if (v.isActive()) {
3854                 v.setActive(false, true);
3855                 prev = v;
3856                 
3857             }
3858             
3859         });
3860
3861         item.setActive(true, true);
3862         this.fireEvent('changed', this, item, prev);
3863         
3864         
3865     },
3866     /**
3867     * gets the active Navigation item
3868     * @return {Roo.bootstrap.NavItem} the current navitem
3869     */
3870     getActive : function()
3871     {
3872         
3873         var prev = false;
3874         Roo.each(this.navItems, function(v){
3875             
3876             if (v.isActive()) {
3877                 prev = v;
3878                 
3879             }
3880             
3881         });
3882         return prev;
3883     },
3884     
3885     indexOfNav : function()
3886     {
3887         
3888         var prev = false;
3889         Roo.each(this.navItems, function(v,i){
3890             
3891             if (v.isActive()) {
3892                 prev = i;
3893                 
3894             }
3895             
3896         });
3897         return prev;
3898     },
3899     /**
3900     * adds a Navigation item
3901     * @param {Roo.bootstrap.NavItem} the navitem to add
3902     */
3903     addItem : function(cfg)
3904     {
3905         var cn = new Roo.bootstrap.NavItem(cfg);
3906         this.register(cn);
3907         cn.parentId = this.id;
3908         cn.onRender(this.el, null);
3909         return cn;
3910     },
3911     /**
3912     * register a Navigation item
3913     * @param {Roo.bootstrap.NavItem} the navitem to add
3914     */
3915     register : function(item)
3916     {
3917         this.navItems.push( item);
3918         item.navId = this.navId;
3919     
3920     },
3921     
3922     /**
3923     * clear all the Navigation item
3924     */
3925    
3926     clearAll : function()
3927     {
3928         this.navItems = [];
3929         this.el.dom.innerHTML = '';
3930     },
3931     
3932     getNavItem: function(tabId)
3933     {
3934         var ret = false;
3935         Roo.each(this.navItems, function(e) {
3936             if (e.tabId == tabId) {
3937                ret =  e;
3938                return false;
3939             }
3940             return true;
3941             
3942         });
3943         return ret;
3944     },
3945     
3946     setActiveNext : function()
3947     {
3948         var i = this.indexOfNav(this.getActive());
3949         if (i > this.navItems.length) {
3950             return;
3951         }
3952         this.setActiveItem(this.navItems[i+1]);
3953     },
3954     setActivePrev : function()
3955     {
3956         var i = this.indexOfNav(this.getActive());
3957         if (i  < 1) {
3958             return;
3959         }
3960         this.setActiveItem(this.navItems[i-1]);
3961     },
3962     clearWasActive : function(except) {
3963         Roo.each(this.navItems, function(e) {
3964             if (e.tabId != except.tabId && e.was_active) {
3965                e.was_active = false;
3966                return false;
3967             }
3968             return true;
3969             
3970         });
3971     },
3972     getWasActive : function ()
3973     {
3974         var r = false;
3975         Roo.each(this.navItems, function(e) {
3976             if (e.was_active) {
3977                r = e;
3978                return false;
3979             }
3980             return true;
3981             
3982         });
3983         return r;
3984     }
3985     
3986     
3987 });
3988
3989  
3990 Roo.apply(Roo.bootstrap.NavGroup, {
3991     
3992     groups: {},
3993      /**
3994     * register a Navigation Group
3995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3996     */
3997     register : function(navgrp)
3998     {
3999         this.groups[navgrp.navId] = navgrp;
4000         
4001     },
4002     /**
4003     * fetch a Navigation Group based on the navigation ID
4004     * @param {string} the navgroup to add
4005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4006     */
4007     get: function(navId) {
4008         if (typeof(this.groups[navId]) == 'undefined') {
4009             return false;
4010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4011         }
4012         return this.groups[navId] ;
4013     }
4014     
4015     
4016     
4017 });
4018
4019  /*
4020  * - LGPL
4021  *
4022  * row
4023  * 
4024  */
4025
4026 /**
4027  * @class Roo.bootstrap.NavItem
4028  * @extends Roo.bootstrap.Component
4029  * Bootstrap Navbar.NavItem class
4030  * @cfg {String} href  link to
4031  * @cfg {String} html content of button
4032  * @cfg {String} badge text inside badge
4033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4034  * @cfg {String} glyphicon name of glyphicon
4035  * @cfg {String} icon name of font awesome icon
4036  * @cfg {Boolean} active Is item active
4037  * @cfg {Boolean} disabled Is item disabled
4038  
4039  * @cfg {Boolean} preventDefault (true | false) default false
4040  * @cfg {String} tabId the tab that this item activates.
4041  * @cfg {String} tagtype (a|span) render as a href or span?
4042  * @cfg {Boolean} animateRef (true|false) link to element default false  
4043   
4044  * @constructor
4045  * Create a new Navbar Item
4046  * @param {Object} config The config object
4047  */
4048 Roo.bootstrap.NavItem = function(config){
4049     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4050     this.addEvents({
4051         // raw events
4052         /**
4053          * @event click
4054          * The raw click event for the entire grid.
4055          * @param {Roo.EventObject} e
4056          */
4057         "click" : true,
4058          /**
4059             * @event changed
4060             * Fires when the active item active state changes
4061             * @param {Roo.bootstrap.NavItem} this
4062             * @param {boolean} state the new state
4063              
4064          */
4065         'changed': true,
4066         /**
4067             * @event scrollto
4068             * Fires when scroll to element
4069             * @param {Roo.bootstrap.NavItem} this
4070             * @param {Object} options
4071             * @param {Roo.EventObject} e
4072              
4073          */
4074         'scrollto': true
4075     });
4076    
4077 };
4078
4079 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4080     
4081     href: false,
4082     html: '',
4083     badge: '',
4084     icon: false,
4085     glyphicon: false,
4086     active: false,
4087     preventDefault : false,
4088     tabId : false,
4089     tagtype : 'a',
4090     disabled : false,
4091     animateRef : false,
4092     was_active : false,
4093     
4094     getAutoCreate : function(){
4095          
4096         var cfg = {
4097             tag: 'li',
4098             cls: 'nav-item'
4099             
4100         };
4101         
4102         if (this.active) {
4103             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4104         }
4105         if (this.disabled) {
4106             cfg.cls += ' disabled';
4107         }
4108         
4109         if (this.href || this.html || this.glyphicon || this.icon) {
4110             cfg.cn = [
4111                 {
4112                     tag: this.tagtype,
4113                     href : this.href || "#",
4114                     html: this.html || ''
4115                 }
4116             ];
4117             
4118             if (this.icon) {
4119                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4120             }
4121
4122             if(this.glyphicon) {
4123                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4124             }
4125             
4126             if (this.menu) {
4127                 
4128                 cfg.cn[0].html += " <span class='caret'></span>";
4129              
4130             }
4131             
4132             if (this.badge !== '') {
4133                  
4134                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4135             }
4136         }
4137         
4138         
4139         
4140         return cfg;
4141     },
4142     initEvents: function() 
4143     {
4144         if (typeof (this.menu) != 'undefined') {
4145             this.menu.parentType = this.xtype;
4146             this.menu.triggerEl = this.el;
4147             this.menu = this.addxtype(Roo.apply({}, this.menu));
4148         }
4149         
4150         this.el.select('a',true).on('click', this.onClick, this);
4151         
4152         if(this.tagtype == 'span'){
4153             this.el.select('span',true).on('click', this.onClick, this);
4154         }
4155        
4156         // at this point parent should be available..
4157         this.parent().register(this);
4158     },
4159     
4160     onClick : function(e)
4161     {
4162         if(
4163                 this.preventDefault || 
4164                 this.href == '#' 
4165         ){
4166             
4167             e.preventDefault();
4168         }
4169         
4170         if (this.disabled) {
4171             return;
4172         }
4173         
4174         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4175         if (tg && tg.transition) {
4176             Roo.log("waiting for the transitionend");
4177             return;
4178         }
4179         
4180         
4181         
4182         //Roo.log("fire event clicked");
4183         if(this.fireEvent('click', this, e) === false){
4184             return;
4185         };
4186         
4187         if(this.tagtype == 'span'){
4188             return;
4189         }
4190         
4191         //Roo.log(this.href);
4192         var ael = this.el.select('a',true).first();
4193         //Roo.log(ael);
4194         
4195         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4196             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4197             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4198                 return; // ignore... - it's a 'hash' to another page.
4199             }
4200             
4201             e.preventDefault();
4202             this.scrollToElement(e);
4203         }
4204         
4205         
4206         var p =  this.parent();
4207    
4208         if (['tabs','pills'].indexOf(p.type)!==-1) {
4209             if (typeof(p.setActiveItem) !== 'undefined') {
4210                 p.setActiveItem(this);
4211             }
4212         }
4213         
4214         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4215         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4216             // remove the collapsed menu expand...
4217             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4218         }
4219     },
4220     
4221     isActive: function () {
4222         return this.active
4223     },
4224     setActive : function(state, fire, is_was_active)
4225     {
4226         if (this.active && !state && this.navId) {
4227             this.was_active = true;
4228             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4229             if (nv) {
4230                 nv.clearWasActive(this);
4231             }
4232             
4233         }
4234         this.active = state;
4235         
4236         if (!state ) {
4237             this.el.removeClass('active');
4238         } else if (!this.el.hasClass('active')) {
4239             this.el.addClass('active');
4240         }
4241         if (fire) {
4242             this.fireEvent('changed', this, state);
4243         }
4244         
4245         // show a panel if it's registered and related..
4246         
4247         if (!this.navId || !this.tabId || !state || is_was_active) {
4248             return;
4249         }
4250         
4251         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4252         if (!tg) {
4253             return;
4254         }
4255         var pan = tg.getPanelByName(this.tabId);
4256         if (!pan) {
4257             return;
4258         }
4259         // if we can not flip to new panel - go back to old nav highlight..
4260         if (false == tg.showPanel(pan)) {
4261             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4262             if (nv) {
4263                 var onav = nv.getWasActive();
4264                 if (onav) {
4265                     onav.setActive(true, false, true);
4266                 }
4267             }
4268             
4269         }
4270         
4271         
4272         
4273     },
4274      // this should not be here...
4275     setDisabled : function(state)
4276     {
4277         this.disabled = state;
4278         if (!state ) {
4279             this.el.removeClass('disabled');
4280         } else if (!this.el.hasClass('disabled')) {
4281             this.el.addClass('disabled');
4282         }
4283         
4284     },
4285     
4286     /**
4287      * Fetch the element to display the tooltip on.
4288      * @return {Roo.Element} defaults to this.el
4289      */
4290     tooltipEl : function()
4291     {
4292         return this.el.select('' + this.tagtype + '', true).first();
4293     },
4294     
4295     scrollToElement : function(e)
4296     {
4297         var c = document.body;
4298         
4299         /*
4300          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4301          */
4302         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4303             c = document.documentElement;
4304         }
4305         
4306         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4307         
4308         if(!target){
4309             return;
4310         }
4311
4312         var o = target.calcOffsetsTo(c);
4313         
4314         var options = {
4315             target : target,
4316             value : o[1]
4317         };
4318         
4319         this.fireEvent('scrollto', this, options, e);
4320         
4321         Roo.get(c).scrollTo('top', options.value, true);
4322         
4323         return;
4324     }
4325 });
4326  
4327
4328  /*
4329  * - LGPL
4330  *
4331  * sidebar item
4332  *
4333  *  li
4334  *    <span> icon </span>
4335  *    <span> text </span>
4336  *    <span>badge </span>
4337  */
4338
4339 /**
4340  * @class Roo.bootstrap.NavSidebarItem
4341  * @extends Roo.bootstrap.NavItem
4342  * Bootstrap Navbar.NavSidebarItem class
4343  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4344  * @constructor
4345  * Create a new Navbar Button
4346  * @param {Object} config The config object
4347  */
4348 Roo.bootstrap.NavSidebarItem = function(config){
4349     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4350     this.addEvents({
4351         // raw events
4352         /**
4353          * @event click
4354          * The raw click event for the entire grid.
4355          * @param {Roo.EventObject} e
4356          */
4357         "click" : true,
4358          /**
4359             * @event changed
4360             * Fires when the active item active state changes
4361             * @param {Roo.bootstrap.NavSidebarItem} this
4362             * @param {boolean} state the new state
4363              
4364          */
4365         'changed': true
4366     });
4367    
4368 };
4369
4370 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4371     
4372     badgeWeight : 'default',
4373     
4374     getAutoCreate : function(){
4375         
4376         
4377         var a = {
4378                 tag: 'a',
4379                 href : this.href || '#',
4380                 cls: '',
4381                 html : '',
4382                 cn : []
4383         };
4384         var cfg = {
4385             tag: 'li',
4386             cls: '',
4387             cn: [ a ]
4388         };
4389         var span = {
4390             tag: 'span',
4391             html : this.html || ''
4392         };
4393         
4394         
4395         if (this.active) {
4396             cfg.cls += ' active';
4397         }
4398         
4399         if (this.disabled) {
4400             cfg.cls += ' disabled';
4401         }
4402         
4403         // left icon..
4404         if (this.glyphicon || this.icon) {
4405             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4406             a.cn.push({ tag : 'i', cls : c }) ;
4407         }
4408         // html..
4409         a.cn.push(span);
4410         // then badge..
4411         if (this.badge !== '') {
4412             
4413             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4414         }
4415         // fi
4416         if (this.menu) {
4417             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4418             a.cls += 'dropdown-toggle treeview' ;
4419             
4420         }
4421         
4422         
4423         
4424         return cfg;
4425          
4426            
4427     },
4428     
4429     initEvents : function()
4430     { 
4431         this.el.on('click', this.onClick, this);
4432        
4433     
4434         if(this.badge !== ''){
4435  
4436             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4437         }
4438         
4439     },
4440     
4441     onClick : function(e)
4442     {
4443         if(this.disabled){
4444             e.preventDefault();
4445             return;
4446         }
4447         
4448         if(this.preventDefault){
4449             e.preventDefault();
4450         }
4451         
4452         this.fireEvent('click', this);
4453     },
4454     
4455     disable : function()
4456     {
4457         this.setDisabled(true);
4458     },
4459     
4460     enable : function()
4461     {
4462         this.setDisabled(false);
4463     },
4464     
4465     setDisabled : function(state)
4466     {
4467         if(this.disabled == state){
4468             return;
4469         }
4470         
4471         this.disabled = state;
4472         
4473         if (state) {
4474             this.el.addClass('disabled');
4475             return;
4476         }
4477         
4478         this.el.removeClass('disabled');
4479         
4480         return;
4481     },
4482     
4483     setActive : function(state)
4484     {
4485         if(this.active == state){
4486             return;
4487         }
4488         
4489         this.active = state;
4490         
4491         if (state) {
4492             this.el.addClass('active');
4493             return;
4494         }
4495         
4496         this.el.removeClass('active');
4497         
4498         return;
4499     },
4500     
4501     isActive: function () 
4502     {
4503         return this.active;
4504     },
4505     
4506     setBadge : function(str)
4507     {
4508         if(!this.badgeEl){
4509             return;
4510         }
4511         
4512         this.badgeEl.dom.innerHTML = str;
4513     }
4514     
4515    
4516      
4517  
4518 });
4519  
4520
4521  /*
4522  * - LGPL
4523  *
4524  * row
4525  * 
4526  */
4527
4528 /**
4529  * @class Roo.bootstrap.Row
4530  * @extends Roo.bootstrap.Component
4531  * Bootstrap Row class (contains columns...)
4532  * 
4533  * @constructor
4534  * Create a new Row
4535  * @param {Object} config The config object
4536  */
4537
4538 Roo.bootstrap.Row = function(config){
4539     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4540 };
4541
4542 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4543     
4544     getAutoCreate : function(){
4545        return {
4546             cls: 'row clearfix'
4547        };
4548     }
4549     
4550     
4551 });
4552
4553  
4554
4555  /*
4556  * - LGPL
4557  *
4558  * element
4559  * 
4560  */
4561
4562 /**
4563  * @class Roo.bootstrap.Element
4564  * @extends Roo.bootstrap.Component
4565  * Bootstrap Element class
4566  * @cfg {String} html contents of the element
4567  * @cfg {String} tag tag of the element
4568  * @cfg {String} cls class of the element
4569  * @cfg {Boolean} preventDefault (true|false) default false
4570  * @cfg {Boolean} clickable (true|false) default false
4571  * 
4572  * @constructor
4573  * Create a new Element
4574  * @param {Object} config The config object
4575  */
4576
4577 Roo.bootstrap.Element = function(config){
4578     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4579     
4580     this.addEvents({
4581         // raw events
4582         /**
4583          * @event click
4584          * When a element is chick
4585          * @param {Roo.bootstrap.Element} this
4586          * @param {Roo.EventObject} e
4587          */
4588         "click" : true
4589     });
4590 };
4591
4592 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4593     
4594     tag: 'div',
4595     cls: '',
4596     html: '',
4597     preventDefault: false, 
4598     clickable: false,
4599     
4600     getAutoCreate : function(){
4601         
4602         var cfg = {
4603             tag: this.tag,
4604             cls: this.cls,
4605             html: this.html
4606         };
4607         
4608         return cfg;
4609     },
4610     
4611     initEvents: function() 
4612     {
4613         Roo.bootstrap.Element.superclass.initEvents.call(this);
4614         
4615         if(this.clickable){
4616             this.el.on('click', this.onClick, this);
4617         }
4618         
4619     },
4620     
4621     onClick : function(e)
4622     {
4623         if(this.preventDefault){
4624             e.preventDefault();
4625         }
4626         
4627         this.fireEvent('click', this, e);
4628     },
4629     
4630     getValue : function()
4631     {
4632         return this.el.dom.innerHTML;
4633     },
4634     
4635     setValue : function(value)
4636     {
4637         this.el.dom.innerHTML = value;
4638     }
4639    
4640 });
4641
4642  
4643
4644  /*
4645  * - LGPL
4646  *
4647  * pagination
4648  * 
4649  */
4650
4651 /**
4652  * @class Roo.bootstrap.Pagination
4653  * @extends Roo.bootstrap.Component
4654  * Bootstrap Pagination class
4655  * @cfg {String} size xs | sm | md | lg
4656  * @cfg {Boolean} inverse false | true
4657  * 
4658  * @constructor
4659  * Create a new Pagination
4660  * @param {Object} config The config object
4661  */
4662
4663 Roo.bootstrap.Pagination = function(config){
4664     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4665 };
4666
4667 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4668     
4669     cls: false,
4670     size: false,
4671     inverse: false,
4672     
4673     getAutoCreate : function(){
4674         var cfg = {
4675             tag: 'ul',
4676                 cls: 'pagination'
4677         };
4678         if (this.inverse) {
4679             cfg.cls += ' inverse';
4680         }
4681         if (this.html) {
4682             cfg.html=this.html;
4683         }
4684         if (this.cls) {
4685             cfg.cls += " " + this.cls;
4686         }
4687         return cfg;
4688     }
4689    
4690 });
4691
4692  
4693
4694  /*
4695  * - LGPL
4696  *
4697  * Pagination item
4698  * 
4699  */
4700
4701
4702 /**
4703  * @class Roo.bootstrap.PaginationItem
4704  * @extends Roo.bootstrap.Component
4705  * Bootstrap PaginationItem class
4706  * @cfg {String} html text
4707  * @cfg {String} href the link
4708  * @cfg {Boolean} preventDefault (true | false) default true
4709  * @cfg {Boolean} active (true | false) default false
4710  * @cfg {Boolean} disabled default false
4711  * 
4712  * 
4713  * @constructor
4714  * Create a new PaginationItem
4715  * @param {Object} config The config object
4716  */
4717
4718
4719 Roo.bootstrap.PaginationItem = function(config){
4720     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4721     this.addEvents({
4722         // raw events
4723         /**
4724          * @event click
4725          * The raw click event for the entire grid.
4726          * @param {Roo.EventObject} e
4727          */
4728         "click" : true
4729     });
4730 };
4731
4732 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4733     
4734     href : false,
4735     html : false,
4736     preventDefault: true,
4737     active : false,
4738     cls : false,
4739     disabled: false,
4740     
4741     getAutoCreate : function(){
4742         var cfg= {
4743             tag: 'li',
4744             cn: [
4745                 {
4746                     tag : 'a',
4747                     href : this.href ? this.href : '#',
4748                     html : this.html ? this.html : ''
4749                 }
4750             ]
4751         };
4752         
4753         if(this.cls){
4754             cfg.cls = this.cls;
4755         }
4756         
4757         if(this.disabled){
4758             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4759         }
4760         
4761         if(this.active){
4762             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4763         }
4764         
4765         return cfg;
4766     },
4767     
4768     initEvents: function() {
4769         
4770         this.el.on('click', this.onClick, this);
4771         
4772     },
4773     onClick : function(e)
4774     {
4775         Roo.log('PaginationItem on click ');
4776         if(this.preventDefault){
4777             e.preventDefault();
4778         }
4779         
4780         if(this.disabled){
4781             return;
4782         }
4783         
4784         this.fireEvent('click', this, e);
4785     }
4786    
4787 });
4788
4789  
4790
4791  /*
4792  * - LGPL
4793  *
4794  * slider
4795  * 
4796  */
4797
4798
4799 /**
4800  * @class Roo.bootstrap.Slider
4801  * @extends Roo.bootstrap.Component
4802  * Bootstrap Slider class
4803  *    
4804  * @constructor
4805  * Create a new Slider
4806  * @param {Object} config The config object
4807  */
4808
4809 Roo.bootstrap.Slider = function(config){
4810     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4811 };
4812
4813 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4814     
4815     getAutoCreate : function(){
4816         
4817         var cfg = {
4818             tag: 'div',
4819             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4820             cn: [
4821                 {
4822                     tag: 'a',
4823                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4824                 }
4825             ]
4826         };
4827         
4828         return cfg;
4829     }
4830    
4831 });
4832
4833  /*
4834  * Based on:
4835  * Ext JS Library 1.1.1
4836  * Copyright(c) 2006-2007, Ext JS, LLC.
4837  *
4838  * Originally Released Under LGPL - original licence link has changed is not relivant.
4839  *
4840  * Fork - LGPL
4841  * <script type="text/javascript">
4842  */
4843  
4844
4845 /**
4846  * @class Roo.grid.ColumnModel
4847  * @extends Roo.util.Observable
4848  * This is the default implementation of a ColumnModel used by the Grid. It defines
4849  * the columns in the grid.
4850  * <br>Usage:<br>
4851  <pre><code>
4852  var colModel = new Roo.grid.ColumnModel([
4853         {header: "Ticker", width: 60, sortable: true, locked: true},
4854         {header: "Company Name", width: 150, sortable: true},
4855         {header: "Market Cap.", width: 100, sortable: true},
4856         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4857         {header: "Employees", width: 100, sortable: true, resizable: false}
4858  ]);
4859  </code></pre>
4860  * <p>
4861  
4862  * The config options listed for this class are options which may appear in each
4863  * individual column definition.
4864  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4865  * @constructor
4866  * @param {Object} config An Array of column config objects. See this class's
4867  * config objects for details.
4868 */
4869 Roo.grid.ColumnModel = function(config){
4870         /**
4871      * The config passed into the constructor
4872      */
4873     this.config = config;
4874     this.lookup = {};
4875
4876     // if no id, create one
4877     // if the column does not have a dataIndex mapping,
4878     // map it to the order it is in the config
4879     for(var i = 0, len = config.length; i < len; i++){
4880         var c = config[i];
4881         if(typeof c.dataIndex == "undefined"){
4882             c.dataIndex = i;
4883         }
4884         if(typeof c.renderer == "string"){
4885             c.renderer = Roo.util.Format[c.renderer];
4886         }
4887         if(typeof c.id == "undefined"){
4888             c.id = Roo.id();
4889         }
4890         if(c.editor && c.editor.xtype){
4891             c.editor  = Roo.factory(c.editor, Roo.grid);
4892         }
4893         if(c.editor && c.editor.isFormField){
4894             c.editor = new Roo.grid.GridEditor(c.editor);
4895         }
4896         this.lookup[c.id] = c;
4897     }
4898
4899     /**
4900      * The width of columns which have no width specified (defaults to 100)
4901      * @type Number
4902      */
4903     this.defaultWidth = 100;
4904
4905     /**
4906      * Default sortable of columns which have no sortable specified (defaults to false)
4907      * @type Boolean
4908      */
4909     this.defaultSortable = false;
4910
4911     this.addEvents({
4912         /**
4913              * @event widthchange
4914              * Fires when the width of a column changes.
4915              * @param {ColumnModel} this
4916              * @param {Number} columnIndex The column index
4917              * @param {Number} newWidth The new width
4918              */
4919             "widthchange": true,
4920         /**
4921              * @event headerchange
4922              * Fires when the text of a header changes.
4923              * @param {ColumnModel} this
4924              * @param {Number} columnIndex The column index
4925              * @param {Number} newText The new header text
4926              */
4927             "headerchange": true,
4928         /**
4929              * @event hiddenchange
4930              * Fires when a column is hidden or "unhidden".
4931              * @param {ColumnModel} this
4932              * @param {Number} columnIndex The column index
4933              * @param {Boolean} hidden true if hidden, false otherwise
4934              */
4935             "hiddenchange": true,
4936             /**
4937          * @event columnmoved
4938          * Fires when a column is moved.
4939          * @param {ColumnModel} this
4940          * @param {Number} oldIndex
4941          * @param {Number} newIndex
4942          */
4943         "columnmoved" : true,
4944         /**
4945          * @event columlockchange
4946          * Fires when a column's locked state is changed
4947          * @param {ColumnModel} this
4948          * @param {Number} colIndex
4949          * @param {Boolean} locked true if locked
4950          */
4951         "columnlockchange" : true
4952     });
4953     Roo.grid.ColumnModel.superclass.constructor.call(this);
4954 };
4955 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4956     /**
4957      * @cfg {String} header The header text to display in the Grid view.
4958      */
4959     /**
4960      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4961      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4962      * specified, the column's index is used as an index into the Record's data Array.
4963      */
4964     /**
4965      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4966      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4967      */
4968     /**
4969      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4970      * Defaults to the value of the {@link #defaultSortable} property.
4971      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4972      */
4973     /**
4974      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4975      */
4976     /**
4977      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4978      */
4979     /**
4980      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4981      */
4982     /**
4983      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4984      */
4985     /**
4986      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4987      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4988      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4989      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4990      */
4991        /**
4992      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4993      */
4994     /**
4995      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4996      */
4997     /**
4998      * @cfg {String} cursor (Optional)
4999      */
5000     /**
5001      * @cfg {String} tooltip (Optional)
5002      */
5003     /**
5004      * @cfg {Number} xs (Optional)
5005      */
5006     /**
5007      * @cfg {Number} sm (Optional)
5008      */
5009     /**
5010      * @cfg {Number} md (Optional)
5011      */
5012     /**
5013      * @cfg {Number} lg (Optional)
5014      */
5015     /**
5016      * Returns the id of the column at the specified index.
5017      * @param {Number} index The column index
5018      * @return {String} the id
5019      */
5020     getColumnId : function(index){
5021         return this.config[index].id;
5022     },
5023
5024     /**
5025      * Returns the column for a specified id.
5026      * @param {String} id The column id
5027      * @return {Object} the column
5028      */
5029     getColumnById : function(id){
5030         return this.lookup[id];
5031     },
5032
5033     
5034     /**
5035      * Returns the column for a specified dataIndex.
5036      * @param {String} dataIndex The column dataIndex
5037      * @return {Object|Boolean} the column or false if not found
5038      */
5039     getColumnByDataIndex: function(dataIndex){
5040         var index = this.findColumnIndex(dataIndex);
5041         return index > -1 ? this.config[index] : false;
5042     },
5043     
5044     /**
5045      * Returns the index for a specified column id.
5046      * @param {String} id The column id
5047      * @return {Number} the index, or -1 if not found
5048      */
5049     getIndexById : function(id){
5050         for(var i = 0, len = this.config.length; i < len; i++){
5051             if(this.config[i].id == id){
5052                 return i;
5053             }
5054         }
5055         return -1;
5056     },
5057     
5058     /**
5059      * Returns the index for a specified column dataIndex.
5060      * @param {String} dataIndex The column dataIndex
5061      * @return {Number} the index, or -1 if not found
5062      */
5063     
5064     findColumnIndex : function(dataIndex){
5065         for(var i = 0, len = this.config.length; i < len; i++){
5066             if(this.config[i].dataIndex == dataIndex){
5067                 return i;
5068             }
5069         }
5070         return -1;
5071     },
5072     
5073     
5074     moveColumn : function(oldIndex, newIndex){
5075         var c = this.config[oldIndex];
5076         this.config.splice(oldIndex, 1);
5077         this.config.splice(newIndex, 0, c);
5078         this.dataMap = null;
5079         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5080     },
5081
5082     isLocked : function(colIndex){
5083         return this.config[colIndex].locked === true;
5084     },
5085
5086     setLocked : function(colIndex, value, suppressEvent){
5087         if(this.isLocked(colIndex) == value){
5088             return;
5089         }
5090         this.config[colIndex].locked = value;
5091         if(!suppressEvent){
5092             this.fireEvent("columnlockchange", this, colIndex, value);
5093         }
5094     },
5095
5096     getTotalLockedWidth : function(){
5097         var totalWidth = 0;
5098         for(var i = 0; i < this.config.length; i++){
5099             if(this.isLocked(i) && !this.isHidden(i)){
5100                 this.totalWidth += this.getColumnWidth(i);
5101             }
5102         }
5103         return totalWidth;
5104     },
5105
5106     getLockedCount : function(){
5107         for(var i = 0, len = this.config.length; i < len; i++){
5108             if(!this.isLocked(i)){
5109                 return i;
5110             }
5111         }
5112     },
5113
5114     /**
5115      * Returns the number of columns.
5116      * @return {Number}
5117      */
5118     getColumnCount : function(visibleOnly){
5119         if(visibleOnly === true){
5120             var c = 0;
5121             for(var i = 0, len = this.config.length; i < len; i++){
5122                 if(!this.isHidden(i)){
5123                     c++;
5124                 }
5125             }
5126             return c;
5127         }
5128         return this.config.length;
5129     },
5130
5131     /**
5132      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5133      * @param {Function} fn
5134      * @param {Object} scope (optional)
5135      * @return {Array} result
5136      */
5137     getColumnsBy : function(fn, scope){
5138         var r = [];
5139         for(var i = 0, len = this.config.length; i < len; i++){
5140             var c = this.config[i];
5141             if(fn.call(scope||this, c, i) === true){
5142                 r[r.length] = c;
5143             }
5144         }
5145         return r;
5146     },
5147
5148     /**
5149      * Returns true if the specified column is sortable.
5150      * @param {Number} col The column index
5151      * @return {Boolean}
5152      */
5153     isSortable : function(col){
5154         if(typeof this.config[col].sortable == "undefined"){
5155             return this.defaultSortable;
5156         }
5157         return this.config[col].sortable;
5158     },
5159
5160     /**
5161      * Returns the rendering (formatting) function defined for the column.
5162      * @param {Number} col The column index.
5163      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5164      */
5165     getRenderer : function(col){
5166         if(!this.config[col].renderer){
5167             return Roo.grid.ColumnModel.defaultRenderer;
5168         }
5169         return this.config[col].renderer;
5170     },
5171
5172     /**
5173      * Sets the rendering (formatting) function for a column.
5174      * @param {Number} col The column index
5175      * @param {Function} fn The function to use to process the cell's raw data
5176      * to return HTML markup for the grid view. The render function is called with
5177      * the following parameters:<ul>
5178      * <li>Data value.</li>
5179      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5180      * <li>css A CSS style string to apply to the table cell.</li>
5181      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5182      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5183      * <li>Row index</li>
5184      * <li>Column index</li>
5185      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5186      */
5187     setRenderer : function(col, fn){
5188         this.config[col].renderer = fn;
5189     },
5190
5191     /**
5192      * Returns the width for the specified column.
5193      * @param {Number} col The column index
5194      * @return {Number}
5195      */
5196     getColumnWidth : function(col){
5197         return this.config[col].width * 1 || this.defaultWidth;
5198     },
5199
5200     /**
5201      * Sets the width for a column.
5202      * @param {Number} col The column index
5203      * @param {Number} width The new width
5204      */
5205     setColumnWidth : function(col, width, suppressEvent){
5206         this.config[col].width = width;
5207         this.totalWidth = null;
5208         if(!suppressEvent){
5209              this.fireEvent("widthchange", this, col, width);
5210         }
5211     },
5212
5213     /**
5214      * Returns the total width of all columns.
5215      * @param {Boolean} includeHidden True to include hidden column widths
5216      * @return {Number}
5217      */
5218     getTotalWidth : function(includeHidden){
5219         if(!this.totalWidth){
5220             this.totalWidth = 0;
5221             for(var i = 0, len = this.config.length; i < len; i++){
5222                 if(includeHidden || !this.isHidden(i)){
5223                     this.totalWidth += this.getColumnWidth(i);
5224                 }
5225             }
5226         }
5227         return this.totalWidth;
5228     },
5229
5230     /**
5231      * Returns the header for the specified column.
5232      * @param {Number} col The column index
5233      * @return {String}
5234      */
5235     getColumnHeader : function(col){
5236         return this.config[col].header;
5237     },
5238
5239     /**
5240      * Sets the header for a column.
5241      * @param {Number} col The column index
5242      * @param {String} header The new header
5243      */
5244     setColumnHeader : function(col, header){
5245         this.config[col].header = header;
5246         this.fireEvent("headerchange", this, col, header);
5247     },
5248
5249     /**
5250      * Returns the tooltip for the specified column.
5251      * @param {Number} col The column index
5252      * @return {String}
5253      */
5254     getColumnTooltip : function(col){
5255             return this.config[col].tooltip;
5256     },
5257     /**
5258      * Sets the tooltip for a column.
5259      * @param {Number} col The column index
5260      * @param {String} tooltip The new tooltip
5261      */
5262     setColumnTooltip : function(col, tooltip){
5263             this.config[col].tooltip = tooltip;
5264     },
5265
5266     /**
5267      * Returns the dataIndex for the specified column.
5268      * @param {Number} col The column index
5269      * @return {Number}
5270      */
5271     getDataIndex : function(col){
5272         return this.config[col].dataIndex;
5273     },
5274
5275     /**
5276      * Sets the dataIndex for a column.
5277      * @param {Number} col The column index
5278      * @param {Number} dataIndex The new dataIndex
5279      */
5280     setDataIndex : function(col, dataIndex){
5281         this.config[col].dataIndex = dataIndex;
5282     },
5283
5284     
5285     
5286     /**
5287      * Returns true if the cell is editable.
5288      * @param {Number} colIndex The column index
5289      * @param {Number} rowIndex The row index
5290      * @return {Boolean}
5291      */
5292     isCellEditable : function(colIndex, rowIndex){
5293         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5294     },
5295
5296     /**
5297      * Returns the editor defined for the cell/column.
5298      * return false or null to disable editing.
5299      * @param {Number} colIndex The column index
5300      * @param {Number} rowIndex The row index
5301      * @return {Object}
5302      */
5303     getCellEditor : function(colIndex, rowIndex){
5304         return this.config[colIndex].editor;
5305     },
5306
5307     /**
5308      * Sets if a column is editable.
5309      * @param {Number} col The column index
5310      * @param {Boolean} editable True if the column is editable
5311      */
5312     setEditable : function(col, editable){
5313         this.config[col].editable = editable;
5314     },
5315
5316
5317     /**
5318      * Returns true if the column is hidden.
5319      * @param {Number} colIndex The column index
5320      * @return {Boolean}
5321      */
5322     isHidden : function(colIndex){
5323         return this.config[colIndex].hidden;
5324     },
5325
5326
5327     /**
5328      * Returns true if the column width cannot be changed
5329      */
5330     isFixed : function(colIndex){
5331         return this.config[colIndex].fixed;
5332     },
5333
5334     /**
5335      * Returns true if the column can be resized
5336      * @return {Boolean}
5337      */
5338     isResizable : function(colIndex){
5339         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5340     },
5341     /**
5342      * Sets if a column is hidden.
5343      * @param {Number} colIndex The column index
5344      * @param {Boolean} hidden True if the column is hidden
5345      */
5346     setHidden : function(colIndex, hidden){
5347         this.config[colIndex].hidden = hidden;
5348         this.totalWidth = null;
5349         this.fireEvent("hiddenchange", this, colIndex, hidden);
5350     },
5351
5352     /**
5353      * Sets the editor for a column.
5354      * @param {Number} col The column index
5355      * @param {Object} editor The editor object
5356      */
5357     setEditor : function(col, editor){
5358         this.config[col].editor = editor;
5359     }
5360 });
5361
5362 Roo.grid.ColumnModel.defaultRenderer = function(value){
5363         if(typeof value == "string" && value.length < 1){
5364             return "&#160;";
5365         }
5366         return value;
5367 };
5368
5369 // Alias for backwards compatibility
5370 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5371 /*
5372  * Based on:
5373  * Ext JS Library 1.1.1
5374  * Copyright(c) 2006-2007, Ext JS, LLC.
5375  *
5376  * Originally Released Under LGPL - original licence link has changed is not relivant.
5377  *
5378  * Fork - LGPL
5379  * <script type="text/javascript">
5380  */
5381  
5382 /**
5383  * @class Roo.LoadMask
5384  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5385  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5386  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5387  * element's UpdateManager load indicator and will be destroyed after the initial load.
5388  * @constructor
5389  * Create a new LoadMask
5390  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5391  * @param {Object} config The config object
5392  */
5393 Roo.LoadMask = function(el, config){
5394     this.el = Roo.get(el);
5395     Roo.apply(this, config);
5396     if(this.store){
5397         this.store.on('beforeload', this.onBeforeLoad, this);
5398         this.store.on('load', this.onLoad, this);
5399         this.store.on('loadexception', this.onLoadException, this);
5400         this.removeMask = false;
5401     }else{
5402         var um = this.el.getUpdateManager();
5403         um.showLoadIndicator = false; // disable the default indicator
5404         um.on('beforeupdate', this.onBeforeLoad, this);
5405         um.on('update', this.onLoad, this);
5406         um.on('failure', this.onLoad, this);
5407         this.removeMask = true;
5408     }
5409 };
5410
5411 Roo.LoadMask.prototype = {
5412     /**
5413      * @cfg {Boolean} removeMask
5414      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5415      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5416      */
5417     /**
5418      * @cfg {String} msg
5419      * The text to display in a centered loading message box (defaults to 'Loading...')
5420      */
5421     msg : 'Loading...',
5422     /**
5423      * @cfg {String} msgCls
5424      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5425      */
5426     msgCls : 'x-mask-loading',
5427
5428     /**
5429      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5430      * @type Boolean
5431      */
5432     disabled: false,
5433
5434     /**
5435      * Disables the mask to prevent it from being displayed
5436      */
5437     disable : function(){
5438        this.disabled = true;
5439     },
5440
5441     /**
5442      * Enables the mask so that it can be displayed
5443      */
5444     enable : function(){
5445         this.disabled = false;
5446     },
5447     
5448     onLoadException : function()
5449     {
5450         Roo.log(arguments);
5451         
5452         if (typeof(arguments[3]) != 'undefined') {
5453             Roo.MessageBox.alert("Error loading",arguments[3]);
5454         } 
5455         /*
5456         try {
5457             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5458                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5459             }   
5460         } catch(e) {
5461             
5462         }
5463         */
5464     
5465         
5466         
5467         this.el.unmask(this.removeMask);
5468     },
5469     // private
5470     onLoad : function()
5471     {
5472         this.el.unmask(this.removeMask);
5473     },
5474
5475     // private
5476     onBeforeLoad : function(){
5477         if(!this.disabled){
5478             this.el.mask(this.msg, this.msgCls);
5479         }
5480     },
5481
5482     // private
5483     destroy : function(){
5484         if(this.store){
5485             this.store.un('beforeload', this.onBeforeLoad, this);
5486             this.store.un('load', this.onLoad, this);
5487             this.store.un('loadexception', this.onLoadException, this);
5488         }else{
5489             var um = this.el.getUpdateManager();
5490             um.un('beforeupdate', this.onBeforeLoad, this);
5491             um.un('update', this.onLoad, this);
5492             um.un('failure', this.onLoad, this);
5493         }
5494     }
5495 };/*
5496  * - LGPL
5497  *
5498  * table
5499  * 
5500  */
5501
5502 /**
5503  * @class Roo.bootstrap.Table
5504  * @extends Roo.bootstrap.Component
5505  * Bootstrap Table class
5506  * @cfg {String} cls table class
5507  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5508  * @cfg {String} bgcolor Specifies the background color for a table
5509  * @cfg {Number} border Specifies whether the table cells should have borders or not
5510  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5511  * @cfg {Number} cellspacing Specifies the space between cells
5512  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5513  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5514  * @cfg {String} sortable Specifies that the table should be sortable
5515  * @cfg {String} summary Specifies a summary of the content of a table
5516  * @cfg {Number} width Specifies the width of a table
5517  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5518  * 
5519  * @cfg {boolean} striped Should the rows be alternative striped
5520  * @cfg {boolean} bordered Add borders to the table
5521  * @cfg {boolean} hover Add hover highlighting
5522  * @cfg {boolean} condensed Format condensed
5523  * @cfg {boolean} responsive Format condensed
5524  * @cfg {Boolean} loadMask (true|false) default false
5525  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5526  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5527  * @cfg {Boolean} rowSelection (true|false) default false
5528  * @cfg {Boolean} cellSelection (true|false) default false
5529  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5530  
5531  * 
5532  * @constructor
5533  * Create a new Table
5534  * @param {Object} config The config object
5535  */
5536
5537 Roo.bootstrap.Table = function(config){
5538     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5539     
5540     // BC...
5541     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5542     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5543     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5544     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5545     
5546     
5547     if (this.sm) {
5548         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5549         this.sm = this.selModel;
5550         this.sm.xmodule = this.xmodule || false;
5551     }
5552     if (this.cm && typeof(this.cm.config) == 'undefined') {
5553         this.colModel = new Roo.grid.ColumnModel(this.cm);
5554         this.cm = this.colModel;
5555         this.cm.xmodule = this.xmodule || false;
5556     }
5557     if (this.store) {
5558         this.store= Roo.factory(this.store, Roo.data);
5559         this.ds = this.store;
5560         this.ds.xmodule = this.xmodule || false;
5561          
5562     }
5563     if (this.footer && this.store) {
5564         this.footer.dataSource = this.ds;
5565         this.footer = Roo.factory(this.footer);
5566     }
5567     
5568     /** @private */
5569     this.addEvents({
5570         /**
5571          * @event cellclick
5572          * Fires when a cell is clicked
5573          * @param {Roo.bootstrap.Table} this
5574          * @param {Roo.Element} el
5575          * @param {Number} rowIndex
5576          * @param {Number} columnIndex
5577          * @param {Roo.EventObject} e
5578          */
5579         "cellclick" : true,
5580         /**
5581          * @event celldblclick
5582          * Fires when a cell is double clicked
5583          * @param {Roo.bootstrap.Table} this
5584          * @param {Roo.Element} el
5585          * @param {Number} rowIndex
5586          * @param {Number} columnIndex
5587          * @param {Roo.EventObject} e
5588          */
5589         "celldblclick" : true,
5590         /**
5591          * @event rowclick
5592          * Fires when a row is clicked
5593          * @param {Roo.bootstrap.Table} this
5594          * @param {Roo.Element} el
5595          * @param {Number} rowIndex
5596          * @param {Roo.EventObject} e
5597          */
5598         "rowclick" : true,
5599         /**
5600          * @event rowdblclick
5601          * Fires when a row is double clicked
5602          * @param {Roo.bootstrap.Table} this
5603          * @param {Roo.Element} el
5604          * @param {Number} rowIndex
5605          * @param {Roo.EventObject} e
5606          */
5607         "rowdblclick" : true,
5608         /**
5609          * @event mouseover
5610          * Fires when a mouseover occur
5611          * @param {Roo.bootstrap.Table} this
5612          * @param {Roo.Element} el
5613          * @param {Number} rowIndex
5614          * @param {Number} columnIndex
5615          * @param {Roo.EventObject} e
5616          */
5617         "mouseover" : true,
5618         /**
5619          * @event mouseout
5620          * Fires when a mouseout occur
5621          * @param {Roo.bootstrap.Table} this
5622          * @param {Roo.Element} el
5623          * @param {Number} rowIndex
5624          * @param {Number} columnIndex
5625          * @param {Roo.EventObject} e
5626          */
5627         "mouseout" : true,
5628         /**
5629          * @event rowclass
5630          * Fires when a row is rendered, so you can change add a style to it.
5631          * @param {Roo.bootstrap.Table} this
5632          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5633          */
5634         'rowclass' : true,
5635           /**
5636          * @event rowsrendered
5637          * Fires when all the  rows have been rendered
5638          * @param {Roo.bootstrap.Table} this
5639          */
5640         'rowsrendered' : true
5641         
5642     });
5643 };
5644
5645 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5646     
5647     cls: false,
5648     align: false,
5649     bgcolor: false,
5650     border: false,
5651     cellpadding: false,
5652     cellspacing: false,
5653     frame: false,
5654     rules: false,
5655     sortable: false,
5656     summary: false,
5657     width: false,
5658     striped : false,
5659     bordered: false,
5660     hover:  false,
5661     condensed : false,
5662     responsive : false,
5663     sm : false,
5664     cm : false,
5665     store : false,
5666     loadMask : false,
5667     footerShow : true,
5668     headerShow : true,
5669   
5670     rowSelection : false,
5671     cellSelection : false,
5672     layout : false,
5673     
5674     // Roo.Element - the tbody
5675     mainBody: false, 
5676     
5677     getAutoCreate : function(){
5678         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5679         
5680         cfg = {
5681             tag: 'table',
5682             cls : 'table',
5683             cn : []
5684         };
5685             
5686         if (this.striped) {
5687             cfg.cls += ' table-striped';
5688         }
5689         
5690         if (this.hover) {
5691             cfg.cls += ' table-hover';
5692         }
5693         if (this.bordered) {
5694             cfg.cls += ' table-bordered';
5695         }
5696         if (this.condensed) {
5697             cfg.cls += ' table-condensed';
5698         }
5699         if (this.responsive) {
5700             cfg.cls += ' table-responsive';
5701         }
5702         
5703         if (this.cls) {
5704             cfg.cls+=  ' ' +this.cls;
5705         }
5706         
5707         // this lot should be simplifed...
5708         
5709         if (this.align) {
5710             cfg.align=this.align;
5711         }
5712         if (this.bgcolor) {
5713             cfg.bgcolor=this.bgcolor;
5714         }
5715         if (this.border) {
5716             cfg.border=this.border;
5717         }
5718         if (this.cellpadding) {
5719             cfg.cellpadding=this.cellpadding;
5720         }
5721         if (this.cellspacing) {
5722             cfg.cellspacing=this.cellspacing;
5723         }
5724         if (this.frame) {
5725             cfg.frame=this.frame;
5726         }
5727         if (this.rules) {
5728             cfg.rules=this.rules;
5729         }
5730         if (this.sortable) {
5731             cfg.sortable=this.sortable;
5732         }
5733         if (this.summary) {
5734             cfg.summary=this.summary;
5735         }
5736         if (this.width) {
5737             cfg.width=this.width;
5738         }
5739         if (this.layout) {
5740             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5741         }
5742         
5743         if(this.store || this.cm){
5744             if(this.headerShow){
5745                 cfg.cn.push(this.renderHeader());
5746             }
5747             
5748             cfg.cn.push(this.renderBody());
5749             
5750             if(this.footerShow){
5751                 cfg.cn.push(this.renderFooter());
5752             }
5753             
5754             cfg.cls+=  ' TableGrid';
5755         }
5756         
5757         return { cn : [ cfg ] };
5758     },
5759     
5760     initEvents : function()
5761     {   
5762         if(!this.store || !this.cm){
5763             return;
5764         }
5765         
5766         //Roo.log('initEvents with ds!!!!');
5767         
5768         this.mainBody = this.el.select('tbody', true).first();
5769         
5770         
5771         var _this = this;
5772         
5773         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5774             e.on('click', _this.sort, _this);
5775         });
5776         
5777         this.el.on("click", this.onClick, this);
5778         this.el.on("dblclick", this.onDblClick, this);
5779         
5780         // why is this done????? = it breaks dialogs??
5781         //this.parent().el.setStyle('position', 'relative');
5782         
5783         
5784         if (this.footer) {
5785             this.footer.parentId = this.id;
5786             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5787         }
5788         
5789         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5790         
5791         this.store.on('load', this.onLoad, this);
5792         this.store.on('beforeload', this.onBeforeLoad, this);
5793         this.store.on('update', this.onUpdate, this);
5794         this.store.on('add', this.onAdd, this);
5795         
5796     },
5797     
5798     onMouseover : function(e, el)
5799     {
5800         var cell = Roo.get(el);
5801         
5802         if(!cell){
5803             return;
5804         }
5805         
5806         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5807             cell = cell.findParent('td', false, true);
5808         }
5809         
5810         var row = cell.findParent('tr', false, true);
5811         var cellIndex = cell.dom.cellIndex;
5812         var rowIndex = row.dom.rowIndex - 1; // start from 0
5813         
5814         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5815         
5816     },
5817     
5818     onMouseout : function(e, el)
5819     {
5820         var cell = Roo.get(el);
5821         
5822         if(!cell){
5823             return;
5824         }
5825         
5826         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5827             cell = cell.findParent('td', false, true);
5828         }
5829         
5830         var row = cell.findParent('tr', false, true);
5831         var cellIndex = cell.dom.cellIndex;
5832         var rowIndex = row.dom.rowIndex - 1; // start from 0
5833         
5834         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5835         
5836     },
5837     
5838     onClick : function(e, el)
5839     {
5840         var cell = Roo.get(el);
5841         
5842         if(!cell || (!this.cellSelection && !this.rowSelection)){
5843             return;
5844         }
5845         
5846         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5847             cell = cell.findParent('td', false, true);
5848         }
5849         
5850         if(!cell || typeof(cell) == 'undefined'){
5851             return;
5852         }
5853         
5854         var row = cell.findParent('tr', false, true);
5855         
5856         if(!row || typeof(row) == 'undefined'){
5857             return;
5858         }
5859         
5860         var cellIndex = cell.dom.cellIndex;
5861         var rowIndex = this.getRowIndex(row);
5862         
5863         // why??? - should these not be based on SelectionModel?
5864         if(this.cellSelection){
5865             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5866         }
5867         
5868         if(this.rowSelection){
5869             this.fireEvent('rowclick', this, row, rowIndex, e);
5870         }
5871         
5872         
5873     },
5874     
5875     onDblClick : function(e,el)
5876     {
5877         var cell = Roo.get(el);
5878         
5879         if(!cell || (!this.CellSelection && !this.RowSelection)){
5880             return;
5881         }
5882         
5883         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5884             cell = cell.findParent('td', false, true);
5885         }
5886         
5887         if(!cell || typeof(cell) == 'undefined'){
5888             return;
5889         }
5890         
5891         var row = cell.findParent('tr', false, true);
5892         
5893         if(!row || typeof(row) == 'undefined'){
5894             return;
5895         }
5896         
5897         var cellIndex = cell.dom.cellIndex;
5898         var rowIndex = this.getRowIndex(row);
5899         
5900         if(this.CellSelection){
5901             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5902         }
5903         
5904         if(this.RowSelection){
5905             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5906         }
5907     },
5908     
5909     sort : function(e,el)
5910     {
5911         var col = Roo.get(el);
5912         
5913         if(!col.hasClass('sortable')){
5914             return;
5915         }
5916         
5917         var sort = col.attr('sort');
5918         var dir = 'ASC';
5919         
5920         if(col.hasClass('glyphicon-arrow-up')){
5921             dir = 'DESC';
5922         }
5923         
5924         this.store.sortInfo = {field : sort, direction : dir};
5925         
5926         if (this.footer) {
5927             Roo.log("calling footer first");
5928             this.footer.onClick('first');
5929         } else {
5930         
5931             this.store.load({ params : { start : 0 } });
5932         }
5933     },
5934     
5935     renderHeader : function()
5936     {
5937         var header = {
5938             tag: 'thead',
5939             cn : []
5940         };
5941         
5942         var cm = this.cm;
5943         
5944         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5945             
5946             var config = cm.config[i];
5947             
5948             var c = {
5949                 tag: 'th',
5950                 style : '',
5951                 html: cm.getColumnHeader(i)
5952             };
5953             
5954             var hh = '';
5955             
5956             if(typeof(config.lgHeader) != 'undefined'){
5957                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5958             }
5959             
5960             if(typeof(config.mdHeader) != 'undefined'){
5961                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5962             }
5963             
5964             if(typeof(config.smHeader) != 'undefined'){
5965                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5966             }
5967             
5968             if(typeof(config.xsHeader) != 'undefined'){
5969                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5970             }
5971             
5972             if(hh.length){
5973                 c.html = hh;
5974             }
5975             
5976             if(typeof(config.tooltip) != 'undefined'){
5977                 c.tooltip = config.tooltip;
5978             }
5979             
5980             if(typeof(config.colspan) != 'undefined'){
5981                 c.colspan = config.colspan;
5982             }
5983             
5984             if(typeof(config.hidden) != 'undefined' && config.hidden){
5985                 c.style += ' display:none;';
5986             }
5987             
5988             if(typeof(config.dataIndex) != 'undefined'){
5989                 c.sort = config.dataIndex;
5990             }
5991             
5992             if(typeof(config.sortable) != 'undefined' && config.sortable){
5993                 c.cls = 'sortable';
5994             }
5995             
5996             if(typeof(config.align) != 'undefined' && config.align.length){
5997                 c.style += ' text-align:' + config.align + ';';
5998             }
5999             
6000             if(typeof(config.width) != 'undefined'){
6001                 c.style += ' width:' + config.width + 'px;';
6002             }
6003             
6004             if(typeof(config.cls) != 'undefined'){
6005                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6006             }
6007             
6008             ['xs','sm','md','lg'].map(function(size){
6009                 
6010                 if(typeof(config[size]) == 'undefined'){
6011                     return;
6012                 }
6013                 
6014                 if (!config[size]) { // 0 = hidden
6015                     cfg.cls += ' hidden-' + size;
6016                     return;
6017                 }
6018                 
6019                 cfg.cls += ' col-' + size + '-' + config[size];
6020
6021             });
6022             
6023             header.cn.push(c)
6024         }
6025         
6026         return header;
6027     },
6028     
6029     renderBody : function()
6030     {
6031         var body = {
6032             tag: 'tbody',
6033             cn : [
6034                 {
6035                     tag: 'tr',
6036                     cn : [
6037                         {
6038                             tag : 'td',
6039                             colspan :  this.cm.getColumnCount()
6040                         }
6041                     ]
6042                 }
6043             ]
6044         };
6045         
6046         return body;
6047     },
6048     
6049     renderFooter : function()
6050     {
6051         var footer = {
6052             tag: 'tfoot',
6053             cn : [
6054                 {
6055                     tag: 'tr',
6056                     cn : [
6057                         {
6058                             tag : 'td',
6059                             colspan :  this.cm.getColumnCount()
6060                         }
6061                     ]
6062                 }
6063             ]
6064         };
6065         
6066         return footer;
6067     },
6068     
6069     
6070     
6071     onLoad : function()
6072     {
6073         Roo.log('ds onload');
6074         this.clear();
6075         
6076         var _this = this;
6077         var cm = this.cm;
6078         var ds = this.store;
6079         
6080         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6081             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6082             
6083             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6084                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6085             }
6086             
6087             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6088                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6089             }
6090         });
6091         
6092         var tbody =  this.mainBody;
6093               
6094         if(ds.getCount() > 0){
6095             ds.data.each(function(d,rowIndex){
6096                 var row =  this.renderRow(cm, ds, rowIndex);
6097                 
6098                 tbody.createChild(row);
6099                 
6100                 var _this = this;
6101                 
6102                 if(row.cellObjects.length){
6103                     Roo.each(row.cellObjects, function(r){
6104                         _this.renderCellObject(r);
6105                     })
6106                 }
6107                 
6108             }, this);
6109         }
6110         
6111         Roo.each(this.el.select('tbody td', true).elements, function(e){
6112             e.on('mouseover', _this.onMouseover, _this);
6113         });
6114         
6115         Roo.each(this.el.select('tbody td', true).elements, function(e){
6116             e.on('mouseout', _this.onMouseout, _this);
6117         });
6118         this.fireEvent('rowsrendered', this);
6119         //if(this.loadMask){
6120         //    this.maskEl.hide();
6121         //}
6122     },
6123     
6124     
6125     onUpdate : function(ds,record)
6126     {
6127         this.refreshRow(record);
6128     },
6129     
6130     onRemove : function(ds, record, index, isUpdate){
6131         if(isUpdate !== true){
6132             this.fireEvent("beforerowremoved", this, index, record);
6133         }
6134         var bt = this.mainBody.dom;
6135         
6136         var rows = this.el.select('tbody > tr', true).elements;
6137         
6138         if(typeof(rows[index]) != 'undefined'){
6139             bt.removeChild(rows[index].dom);
6140         }
6141         
6142 //        if(bt.rows[index]){
6143 //            bt.removeChild(bt.rows[index]);
6144 //        }
6145         
6146         if(isUpdate !== true){
6147             //this.stripeRows(index);
6148             //this.syncRowHeights(index, index);
6149             //this.layout();
6150             this.fireEvent("rowremoved", this, index, record);
6151         }
6152     },
6153     
6154     onAdd : function(ds, records, rowIndex)
6155     {
6156         //Roo.log('on Add called');
6157         // - note this does not handle multiple adding very well..
6158         var bt = this.mainBody.dom;
6159         for (var i =0 ; i < records.length;i++) {
6160             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6161             //Roo.log(records[i]);
6162             //Roo.log(this.store.getAt(rowIndex+i));
6163             this.insertRow(this.store, rowIndex + i, false);
6164             return;
6165         }
6166         
6167     },
6168     
6169     
6170     refreshRow : function(record){
6171         var ds = this.store, index;
6172         if(typeof record == 'number'){
6173             index = record;
6174             record = ds.getAt(index);
6175         }else{
6176             index = ds.indexOf(record);
6177         }
6178         this.insertRow(ds, index, true);
6179         this.onRemove(ds, record, index+1, true);
6180         //this.syncRowHeights(index, index);
6181         //this.layout();
6182         this.fireEvent("rowupdated", this, index, record);
6183     },
6184     
6185     insertRow : function(dm, rowIndex, isUpdate){
6186         
6187         if(!isUpdate){
6188             this.fireEvent("beforerowsinserted", this, rowIndex);
6189         }
6190             //var s = this.getScrollState();
6191         var row = this.renderRow(this.cm, this.store, rowIndex);
6192         // insert before rowIndex..
6193         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6194         
6195         var _this = this;
6196                 
6197         if(row.cellObjects.length){
6198             Roo.each(row.cellObjects, function(r){
6199                 _this.renderCellObject(r);
6200             })
6201         }
6202             
6203         if(!isUpdate){
6204             this.fireEvent("rowsinserted", this, rowIndex);
6205             //this.syncRowHeights(firstRow, lastRow);
6206             //this.stripeRows(firstRow);
6207             //this.layout();
6208         }
6209         
6210     },
6211     
6212     
6213     getRowDom : function(rowIndex)
6214     {
6215         var rows = this.el.select('tbody > tr', true).elements;
6216         
6217         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6218         
6219     },
6220     // returns the object tree for a tr..
6221   
6222     
6223     renderRow : function(cm, ds, rowIndex) 
6224     {
6225         
6226         var d = ds.getAt(rowIndex);
6227         
6228         var row = {
6229             tag : 'tr',
6230             cn : []
6231         };
6232             
6233         var cellObjects = [];
6234         
6235         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6236             var config = cm.config[i];
6237             
6238             var renderer = cm.getRenderer(i);
6239             var value = '';
6240             var id = false;
6241             
6242             if(typeof(renderer) !== 'undefined'){
6243                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6244             }
6245             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6246             // and are rendered into the cells after the row is rendered - using the id for the element.
6247             
6248             if(typeof(value) === 'object'){
6249                 id = Roo.id();
6250                 cellObjects.push({
6251                     container : id,
6252                     cfg : value 
6253                 })
6254             }
6255             
6256             var rowcfg = {
6257                 record: d,
6258                 rowIndex : rowIndex,
6259                 colIndex : i,
6260                 rowClass : ''
6261             };
6262
6263             this.fireEvent('rowclass', this, rowcfg);
6264             
6265             var td = {
6266                 tag: 'td',
6267                 cls : rowcfg.rowClass,
6268                 style: '',
6269                 html: (typeof(value) === 'object') ? '' : value
6270             };
6271             
6272             if (id) {
6273                 td.id = id;
6274             }
6275             
6276             if(typeof(config.colspan) != 'undefined'){
6277                 td.colspan = config.colspan;
6278             }
6279             
6280             if(typeof(config.hidden) != 'undefined' && config.hidden){
6281                 td.style += ' display:none;';
6282             }
6283             
6284             if(typeof(config.align) != 'undefined' && config.align.length){
6285                 td.style += ' text-align:' + config.align + ';';
6286             }
6287             
6288             if(typeof(config.width) != 'undefined'){
6289                 td.style += ' width:' +  config.width + 'px;';
6290             }
6291             
6292             if(typeof(config.cursor) != 'undefined'){
6293                 td.style += ' cursor:' +  config.cursor + ';';
6294             }
6295             
6296             if(typeof(config.cls) != 'undefined'){
6297                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6298             }
6299             
6300             ['xs','sm','md','lg'].map(function(size){
6301                 
6302                 if(typeof(config[size]) == 'undefined'){
6303                     return;
6304                 }
6305                 
6306                 if (!config[size]) { // 0 = hidden
6307                     td.cls += ' hidden-' + size;
6308                     return;
6309                 }
6310                 
6311                 td.cls += ' col-' + size + '-' + config[size];
6312
6313             });
6314              
6315             row.cn.push(td);
6316            
6317         }
6318         
6319         row.cellObjects = cellObjects;
6320         
6321         return row;
6322           
6323     },
6324     
6325     
6326     
6327     onBeforeLoad : function()
6328     {
6329         //Roo.log('ds onBeforeLoad');
6330         
6331         //this.clear();
6332         
6333         //if(this.loadMask){
6334         //    this.maskEl.show();
6335         //}
6336     },
6337      /**
6338      * Remove all rows
6339      */
6340     clear : function()
6341     {
6342         this.el.select('tbody', true).first().dom.innerHTML = '';
6343     },
6344     /**
6345      * Show or hide a row.
6346      * @param {Number} rowIndex to show or hide
6347      * @param {Boolean} state hide
6348      */
6349     setRowVisibility : function(rowIndex, state)
6350     {
6351         var bt = this.mainBody.dom;
6352         
6353         var rows = this.el.select('tbody > tr', true).elements;
6354         
6355         if(typeof(rows[rowIndex]) == 'undefined'){
6356             return;
6357         }
6358         rows[rowIndex].dom.style.display = state ? '' : 'none';
6359     },
6360     
6361     
6362     getSelectionModel : function(){
6363         if(!this.selModel){
6364             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6365         }
6366         return this.selModel;
6367     },
6368     /*
6369      * Render the Roo.bootstrap object from renderder
6370      */
6371     renderCellObject : function(r)
6372     {
6373         var _this = this;
6374         
6375         var t = r.cfg.render(r.container);
6376         
6377         if(r.cfg.cn){
6378             Roo.each(r.cfg.cn, function(c){
6379                 var child = {
6380                     container: t.getChildContainer(),
6381                     cfg: c
6382                 };
6383                 _this.renderCellObject(child);
6384             })
6385         }
6386     },
6387     
6388     getRowIndex : function(row)
6389     {
6390         var rowIndex = -1;
6391         
6392         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6393             if(el != row){
6394                 return;
6395             }
6396             
6397             rowIndex = index;
6398         });
6399         
6400         return rowIndex;
6401     }
6402    
6403 });
6404
6405  
6406
6407  /*
6408  * - LGPL
6409  *
6410  * table cell
6411  * 
6412  */
6413
6414 /**
6415  * @class Roo.bootstrap.TableCell
6416  * @extends Roo.bootstrap.Component
6417  * Bootstrap TableCell class
6418  * @cfg {String} html cell contain text
6419  * @cfg {String} cls cell class
6420  * @cfg {String} tag cell tag (td|th) default td
6421  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6422  * @cfg {String} align Aligns the content in a cell
6423  * @cfg {String} axis Categorizes cells
6424  * @cfg {String} bgcolor Specifies the background color of a cell
6425  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6426  * @cfg {Number} colspan Specifies the number of columns a cell should span
6427  * @cfg {String} headers Specifies one or more header cells a cell is related to
6428  * @cfg {Number} height Sets the height of a cell
6429  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6430  * @cfg {Number} rowspan Sets the number of rows a cell should span
6431  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6432  * @cfg {String} valign Vertical aligns the content in a cell
6433  * @cfg {Number} width Specifies the width of a cell
6434  * 
6435  * @constructor
6436  * Create a new TableCell
6437  * @param {Object} config The config object
6438  */
6439
6440 Roo.bootstrap.TableCell = function(config){
6441     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6442 };
6443
6444 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6445     
6446     html: false,
6447     cls: false,
6448     tag: false,
6449     abbr: false,
6450     align: false,
6451     axis: false,
6452     bgcolor: false,
6453     charoff: false,
6454     colspan: false,
6455     headers: false,
6456     height: false,
6457     nowrap: false,
6458     rowspan: false,
6459     scope: false,
6460     valign: false,
6461     width: false,
6462     
6463     
6464     getAutoCreate : function(){
6465         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6466         
6467         cfg = {
6468             tag: 'td'
6469         };
6470         
6471         if(this.tag){
6472             cfg.tag = this.tag;
6473         }
6474         
6475         if (this.html) {
6476             cfg.html=this.html
6477         }
6478         if (this.cls) {
6479             cfg.cls=this.cls
6480         }
6481         if (this.abbr) {
6482             cfg.abbr=this.abbr
6483         }
6484         if (this.align) {
6485             cfg.align=this.align
6486         }
6487         if (this.axis) {
6488             cfg.axis=this.axis
6489         }
6490         if (this.bgcolor) {
6491             cfg.bgcolor=this.bgcolor
6492         }
6493         if (this.charoff) {
6494             cfg.charoff=this.charoff
6495         }
6496         if (this.colspan) {
6497             cfg.colspan=this.colspan
6498         }
6499         if (this.headers) {
6500             cfg.headers=this.headers
6501         }
6502         if (this.height) {
6503             cfg.height=this.height
6504         }
6505         if (this.nowrap) {
6506             cfg.nowrap=this.nowrap
6507         }
6508         if (this.rowspan) {
6509             cfg.rowspan=this.rowspan
6510         }
6511         if (this.scope) {
6512             cfg.scope=this.scope
6513         }
6514         if (this.valign) {
6515             cfg.valign=this.valign
6516         }
6517         if (this.width) {
6518             cfg.width=this.width
6519         }
6520         
6521         
6522         return cfg;
6523     }
6524    
6525 });
6526
6527  
6528
6529  /*
6530  * - LGPL
6531  *
6532  * table row
6533  * 
6534  */
6535
6536 /**
6537  * @class Roo.bootstrap.TableRow
6538  * @extends Roo.bootstrap.Component
6539  * Bootstrap TableRow class
6540  * @cfg {String} cls row class
6541  * @cfg {String} align Aligns the content in a table row
6542  * @cfg {String} bgcolor Specifies a background color for a table row
6543  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6544  * @cfg {String} valign Vertical aligns the content in a table row
6545  * 
6546  * @constructor
6547  * Create a new TableRow
6548  * @param {Object} config The config object
6549  */
6550
6551 Roo.bootstrap.TableRow = function(config){
6552     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6553 };
6554
6555 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6556     
6557     cls: false,
6558     align: false,
6559     bgcolor: false,
6560     charoff: false,
6561     valign: false,
6562     
6563     getAutoCreate : function(){
6564         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6565         
6566         cfg = {
6567             tag: 'tr'
6568         };
6569             
6570         if(this.cls){
6571             cfg.cls = this.cls;
6572         }
6573         if(this.align){
6574             cfg.align = this.align;
6575         }
6576         if(this.bgcolor){
6577             cfg.bgcolor = this.bgcolor;
6578         }
6579         if(this.charoff){
6580             cfg.charoff = this.charoff;
6581         }
6582         if(this.valign){
6583             cfg.valign = this.valign;
6584         }
6585         
6586         return cfg;
6587     }
6588    
6589 });
6590
6591  
6592
6593  /*
6594  * - LGPL
6595  *
6596  * table body
6597  * 
6598  */
6599
6600 /**
6601  * @class Roo.bootstrap.TableBody
6602  * @extends Roo.bootstrap.Component
6603  * Bootstrap TableBody class
6604  * @cfg {String} cls element class
6605  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6606  * @cfg {String} align Aligns the content inside the element
6607  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6608  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6609  * 
6610  * @constructor
6611  * Create a new TableBody
6612  * @param {Object} config The config object
6613  */
6614
6615 Roo.bootstrap.TableBody = function(config){
6616     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6617 };
6618
6619 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6620     
6621     cls: false,
6622     tag: false,
6623     align: false,
6624     charoff: false,
6625     valign: false,
6626     
6627     getAutoCreate : function(){
6628         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6629         
6630         cfg = {
6631             tag: 'tbody'
6632         };
6633             
6634         if (this.cls) {
6635             cfg.cls=this.cls
6636         }
6637         if(this.tag){
6638             cfg.tag = this.tag;
6639         }
6640         
6641         if(this.align){
6642             cfg.align = this.align;
6643         }
6644         if(this.charoff){
6645             cfg.charoff = this.charoff;
6646         }
6647         if(this.valign){
6648             cfg.valign = this.valign;
6649         }
6650         
6651         return cfg;
6652     }
6653     
6654     
6655 //    initEvents : function()
6656 //    {
6657 //        
6658 //        if(!this.store){
6659 //            return;
6660 //        }
6661 //        
6662 //        this.store = Roo.factory(this.store, Roo.data);
6663 //        this.store.on('load', this.onLoad, this);
6664 //        
6665 //        this.store.load();
6666 //        
6667 //    },
6668 //    
6669 //    onLoad: function () 
6670 //    {   
6671 //        this.fireEvent('load', this);
6672 //    }
6673 //    
6674 //   
6675 });
6676
6677  
6678
6679  /*
6680  * Based on:
6681  * Ext JS Library 1.1.1
6682  * Copyright(c) 2006-2007, Ext JS, LLC.
6683  *
6684  * Originally Released Under LGPL - original licence link has changed is not relivant.
6685  *
6686  * Fork - LGPL
6687  * <script type="text/javascript">
6688  */
6689
6690 // as we use this in bootstrap.
6691 Roo.namespace('Roo.form');
6692  /**
6693  * @class Roo.form.Action
6694  * Internal Class used to handle form actions
6695  * @constructor
6696  * @param {Roo.form.BasicForm} el The form element or its id
6697  * @param {Object} config Configuration options
6698  */
6699
6700  
6701  
6702 // define the action interface
6703 Roo.form.Action = function(form, options){
6704     this.form = form;
6705     this.options = options || {};
6706 };
6707 /**
6708  * Client Validation Failed
6709  * @const 
6710  */
6711 Roo.form.Action.CLIENT_INVALID = 'client';
6712 /**
6713  * Server Validation Failed
6714  * @const 
6715  */
6716 Roo.form.Action.SERVER_INVALID = 'server';
6717  /**
6718  * Connect to Server Failed
6719  * @const 
6720  */
6721 Roo.form.Action.CONNECT_FAILURE = 'connect';
6722 /**
6723  * Reading Data from Server Failed
6724  * @const 
6725  */
6726 Roo.form.Action.LOAD_FAILURE = 'load';
6727
6728 Roo.form.Action.prototype = {
6729     type : 'default',
6730     failureType : undefined,
6731     response : undefined,
6732     result : undefined,
6733
6734     // interface method
6735     run : function(options){
6736
6737     },
6738
6739     // interface method
6740     success : function(response){
6741
6742     },
6743
6744     // interface method
6745     handleResponse : function(response){
6746
6747     },
6748
6749     // default connection failure
6750     failure : function(response){
6751         
6752         this.response = response;
6753         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6754         this.form.afterAction(this, false);
6755     },
6756
6757     processResponse : function(response){
6758         this.response = response;
6759         if(!response.responseText){
6760             return true;
6761         }
6762         this.result = this.handleResponse(response);
6763         return this.result;
6764     },
6765
6766     // utility functions used internally
6767     getUrl : function(appendParams){
6768         var url = this.options.url || this.form.url || this.form.el.dom.action;
6769         if(appendParams){
6770             var p = this.getParams();
6771             if(p){
6772                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6773             }
6774         }
6775         return url;
6776     },
6777
6778     getMethod : function(){
6779         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6780     },
6781
6782     getParams : function(){
6783         var bp = this.form.baseParams;
6784         var p = this.options.params;
6785         if(p){
6786             if(typeof p == "object"){
6787                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6788             }else if(typeof p == 'string' && bp){
6789                 p += '&' + Roo.urlEncode(bp);
6790             }
6791         }else if(bp){
6792             p = Roo.urlEncode(bp);
6793         }
6794         return p;
6795     },
6796
6797     createCallback : function(){
6798         return {
6799             success: this.success,
6800             failure: this.failure,
6801             scope: this,
6802             timeout: (this.form.timeout*1000),
6803             upload: this.form.fileUpload ? this.success : undefined
6804         };
6805     }
6806 };
6807
6808 Roo.form.Action.Submit = function(form, options){
6809     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6810 };
6811
6812 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6813     type : 'submit',
6814
6815     haveProgress : false,
6816     uploadComplete : false,
6817     
6818     // uploadProgress indicator.
6819     uploadProgress : function()
6820     {
6821         if (!this.form.progressUrl) {
6822             return;
6823         }
6824         
6825         if (!this.haveProgress) {
6826             Roo.MessageBox.progress("Uploading", "Uploading");
6827         }
6828         if (this.uploadComplete) {
6829            Roo.MessageBox.hide();
6830            return;
6831         }
6832         
6833         this.haveProgress = true;
6834    
6835         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6836         
6837         var c = new Roo.data.Connection();
6838         c.request({
6839             url : this.form.progressUrl,
6840             params: {
6841                 id : uid
6842             },
6843             method: 'GET',
6844             success : function(req){
6845                //console.log(data);
6846                 var rdata = false;
6847                 var edata;
6848                 try  {
6849                    rdata = Roo.decode(req.responseText)
6850                 } catch (e) {
6851                     Roo.log("Invalid data from server..");
6852                     Roo.log(edata);
6853                     return;
6854                 }
6855                 if (!rdata || !rdata.success) {
6856                     Roo.log(rdata);
6857                     Roo.MessageBox.alert(Roo.encode(rdata));
6858                     return;
6859                 }
6860                 var data = rdata.data;
6861                 
6862                 if (this.uploadComplete) {
6863                    Roo.MessageBox.hide();
6864                    return;
6865                 }
6866                    
6867                 if (data){
6868                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6869                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6870                     );
6871                 }
6872                 this.uploadProgress.defer(2000,this);
6873             },
6874        
6875             failure: function(data) {
6876                 Roo.log('progress url failed ');
6877                 Roo.log(data);
6878             },
6879             scope : this
6880         });
6881            
6882     },
6883     
6884     
6885     run : function()
6886     {
6887         // run get Values on the form, so it syncs any secondary forms.
6888         this.form.getValues();
6889         
6890         var o = this.options;
6891         var method = this.getMethod();
6892         var isPost = method == 'POST';
6893         if(o.clientValidation === false || this.form.isValid()){
6894             
6895             if (this.form.progressUrl) {
6896                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6897                     (new Date() * 1) + '' + Math.random());
6898                     
6899             } 
6900             
6901             
6902             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6903                 form:this.form.el.dom,
6904                 url:this.getUrl(!isPost),
6905                 method: method,
6906                 params:isPost ? this.getParams() : null,
6907                 isUpload: this.form.fileUpload
6908             }));
6909             
6910             this.uploadProgress();
6911
6912         }else if (o.clientValidation !== false){ // client validation failed
6913             this.failureType = Roo.form.Action.CLIENT_INVALID;
6914             this.form.afterAction(this, false);
6915         }
6916     },
6917
6918     success : function(response)
6919     {
6920         this.uploadComplete= true;
6921         if (this.haveProgress) {
6922             Roo.MessageBox.hide();
6923         }
6924         
6925         
6926         var result = this.processResponse(response);
6927         if(result === true || result.success){
6928             this.form.afterAction(this, true);
6929             return;
6930         }
6931         if(result.errors){
6932             this.form.markInvalid(result.errors);
6933             this.failureType = Roo.form.Action.SERVER_INVALID;
6934         }
6935         this.form.afterAction(this, false);
6936     },
6937     failure : function(response)
6938     {
6939         this.uploadComplete= true;
6940         if (this.haveProgress) {
6941             Roo.MessageBox.hide();
6942         }
6943         
6944         this.response = response;
6945         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6946         this.form.afterAction(this, false);
6947     },
6948     
6949     handleResponse : function(response){
6950         if(this.form.errorReader){
6951             var rs = this.form.errorReader.read(response);
6952             var errors = [];
6953             if(rs.records){
6954                 for(var i = 0, len = rs.records.length; i < len; i++) {
6955                     var r = rs.records[i];
6956                     errors[i] = r.data;
6957                 }
6958             }
6959             if(errors.length < 1){
6960                 errors = null;
6961             }
6962             return {
6963                 success : rs.success,
6964                 errors : errors
6965             };
6966         }
6967         var ret = false;
6968         try {
6969             ret = Roo.decode(response.responseText);
6970         } catch (e) {
6971             ret = {
6972                 success: false,
6973                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6974                 errors : []
6975             };
6976         }
6977         return ret;
6978         
6979     }
6980 });
6981
6982
6983 Roo.form.Action.Load = function(form, options){
6984     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6985     this.reader = this.form.reader;
6986 };
6987
6988 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6989     type : 'load',
6990
6991     run : function(){
6992         
6993         Roo.Ajax.request(Roo.apply(
6994                 this.createCallback(), {
6995                     method:this.getMethod(),
6996                     url:this.getUrl(false),
6997                     params:this.getParams()
6998         }));
6999     },
7000
7001     success : function(response){
7002         
7003         var result = this.processResponse(response);
7004         if(result === true || !result.success || !result.data){
7005             this.failureType = Roo.form.Action.LOAD_FAILURE;
7006             this.form.afterAction(this, false);
7007             return;
7008         }
7009         this.form.clearInvalid();
7010         this.form.setValues(result.data);
7011         this.form.afterAction(this, true);
7012     },
7013
7014     handleResponse : function(response){
7015         if(this.form.reader){
7016             var rs = this.form.reader.read(response);
7017             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7018             return {
7019                 success : rs.success,
7020                 data : data
7021             };
7022         }
7023         return Roo.decode(response.responseText);
7024     }
7025 });
7026
7027 Roo.form.Action.ACTION_TYPES = {
7028     'load' : Roo.form.Action.Load,
7029     'submit' : Roo.form.Action.Submit
7030 };/*
7031  * - LGPL
7032  *
7033  * form
7034  * 
7035  */
7036
7037 /**
7038  * @class Roo.bootstrap.Form
7039  * @extends Roo.bootstrap.Component
7040  * Bootstrap Form class
7041  * @cfg {String} method  GET | POST (default POST)
7042  * @cfg {String} labelAlign top | left (default top)
7043  * @cfg {String} align left  | right - for navbars
7044  * @cfg {Boolean} loadMask load mask when submit (default true)
7045
7046  * 
7047  * @constructor
7048  * Create a new Form
7049  * @param {Object} config The config object
7050  */
7051
7052
7053 Roo.bootstrap.Form = function(config){
7054     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7055     this.addEvents({
7056         /**
7057          * @event clientvalidation
7058          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7059          * @param {Form} this
7060          * @param {Boolean} valid true if the form has passed client-side validation
7061          */
7062         clientvalidation: true,
7063         /**
7064          * @event beforeaction
7065          * Fires before any action is performed. Return false to cancel the action.
7066          * @param {Form} this
7067          * @param {Action} action The action to be performed
7068          */
7069         beforeaction: true,
7070         /**
7071          * @event actionfailed
7072          * Fires when an action fails.
7073          * @param {Form} this
7074          * @param {Action} action The action that failed
7075          */
7076         actionfailed : true,
7077         /**
7078          * @event actioncomplete
7079          * Fires when an action is completed.
7080          * @param {Form} this
7081          * @param {Action} action The action that completed
7082          */
7083         actioncomplete : true
7084     });
7085     
7086 };
7087
7088 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7089       
7090      /**
7091      * @cfg {String} method
7092      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7093      */
7094     method : 'POST',
7095     /**
7096      * @cfg {String} url
7097      * The URL to use for form actions if one isn't supplied in the action options.
7098      */
7099     /**
7100      * @cfg {Boolean} fileUpload
7101      * Set to true if this form is a file upload.
7102      */
7103      
7104     /**
7105      * @cfg {Object} baseParams
7106      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7107      */
7108       
7109     /**
7110      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7111      */
7112     timeout: 30,
7113     /**
7114      * @cfg {Sting} align (left|right) for navbar forms
7115      */
7116     align : 'left',
7117
7118     // private
7119     activeAction : null,
7120  
7121     /**
7122      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7123      * element by passing it or its id or mask the form itself by passing in true.
7124      * @type Mixed
7125      */
7126     waitMsgTarget : false,
7127     
7128     loadMask : true,
7129     
7130     getAutoCreate : function(){
7131         
7132         var cfg = {
7133             tag: 'form',
7134             method : this.method || 'POST',
7135             id : this.id || Roo.id(),
7136             cls : ''
7137         };
7138         if (this.parent().xtype.match(/^Nav/)) {
7139             cfg.cls = 'navbar-form navbar-' + this.align;
7140             
7141         }
7142         
7143         if (this.labelAlign == 'left' ) {
7144             cfg.cls += ' form-horizontal';
7145         }
7146         
7147         
7148         return cfg;
7149     },
7150     initEvents : function()
7151     {
7152         this.el.on('submit', this.onSubmit, this);
7153         // this was added as random key presses on the form where triggering form submit.
7154         this.el.on('keypress', function(e) {
7155             if (e.getCharCode() != 13) {
7156                 return true;
7157             }
7158             // we might need to allow it for textareas.. and some other items.
7159             // check e.getTarget().
7160             
7161             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7162                 return true;
7163             }
7164         
7165             Roo.log("keypress blocked");
7166             
7167             e.preventDefault();
7168             return false;
7169         });
7170         
7171     },
7172     // private
7173     onSubmit : function(e){
7174         e.stopEvent();
7175     },
7176     
7177      /**
7178      * Returns true if client-side validation on the form is successful.
7179      * @return Boolean
7180      */
7181     isValid : function(){
7182         var items = this.getItems();
7183         var valid = true;
7184         items.each(function(f){
7185            if(!f.validate()){
7186                valid = false;
7187                
7188            }
7189         });
7190         return valid;
7191     },
7192     /**
7193      * Returns true if any fields in this form have changed since their original load.
7194      * @return Boolean
7195      */
7196     isDirty : function(){
7197         var dirty = false;
7198         var items = this.getItems();
7199         items.each(function(f){
7200            if(f.isDirty()){
7201                dirty = true;
7202                return false;
7203            }
7204            return true;
7205         });
7206         return dirty;
7207     },
7208      /**
7209      * Performs a predefined action (submit or load) or custom actions you define on this form.
7210      * @param {String} actionName The name of the action type
7211      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7212      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7213      * accept other config options):
7214      * <pre>
7215 Property          Type             Description
7216 ----------------  ---------------  ----------------------------------------------------------------------------------
7217 url               String           The url for the action (defaults to the form's url)
7218 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7219 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7220 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7221                                    validate the form on the client (defaults to false)
7222      * </pre>
7223      * @return {BasicForm} this
7224      */
7225     doAction : function(action, options){
7226         if(typeof action == 'string'){
7227             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7228         }
7229         if(this.fireEvent('beforeaction', this, action) !== false){
7230             this.beforeAction(action);
7231             action.run.defer(100, action);
7232         }
7233         return this;
7234     },
7235     
7236     // private
7237     beforeAction : function(action){
7238         var o = action.options;
7239         
7240         if(this.loadMask){
7241             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7242         }
7243         // not really supported yet.. ??
7244         
7245         //if(this.waitMsgTarget === true){
7246         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7247         //}else if(this.waitMsgTarget){
7248         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7249         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7250         //}else {
7251         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7252        // }
7253          
7254     },
7255
7256     // private
7257     afterAction : function(action, success){
7258         this.activeAction = null;
7259         var o = action.options;
7260         
7261         //if(this.waitMsgTarget === true){
7262             this.el.unmask();
7263         //}else if(this.waitMsgTarget){
7264         //    this.waitMsgTarget.unmask();
7265         //}else{
7266         //    Roo.MessageBox.updateProgress(1);
7267         //    Roo.MessageBox.hide();
7268        // }
7269         // 
7270         if(success){
7271             if(o.reset){
7272                 this.reset();
7273             }
7274             Roo.callback(o.success, o.scope, [this, action]);
7275             this.fireEvent('actioncomplete', this, action);
7276             
7277         }else{
7278             
7279             // failure condition..
7280             // we have a scenario where updates need confirming.
7281             // eg. if a locking scenario exists..
7282             // we look for { errors : { needs_confirm : true }} in the response.
7283             if (
7284                 (typeof(action.result) != 'undefined')  &&
7285                 (typeof(action.result.errors) != 'undefined')  &&
7286                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7287            ){
7288                 var _t = this;
7289                 Roo.log("not supported yet");
7290                  /*
7291                 
7292                 Roo.MessageBox.confirm(
7293                     "Change requires confirmation",
7294                     action.result.errorMsg,
7295                     function(r) {
7296                         if (r != 'yes') {
7297                             return;
7298                         }
7299                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7300                     }
7301                     
7302                 );
7303                 */
7304                 
7305                 
7306                 return;
7307             }
7308             
7309             Roo.callback(o.failure, o.scope, [this, action]);
7310             // show an error message if no failed handler is set..
7311             if (!this.hasListener('actionfailed')) {
7312                 Roo.log("need to add dialog support");
7313                 /*
7314                 Roo.MessageBox.alert("Error",
7315                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7316                         action.result.errorMsg :
7317                         "Saving Failed, please check your entries or try again"
7318                 );
7319                 */
7320             }
7321             
7322             this.fireEvent('actionfailed', this, action);
7323         }
7324         
7325     },
7326     /**
7327      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7328      * @param {String} id The value to search for
7329      * @return Field
7330      */
7331     findField : function(id){
7332         var items = this.getItems();
7333         var field = items.get(id);
7334         if(!field){
7335              items.each(function(f){
7336                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7337                     field = f;
7338                     return false;
7339                 }
7340                 return true;
7341             });
7342         }
7343         return field || null;
7344     },
7345      /**
7346      * Mark fields in this form invalid in bulk.
7347      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7348      * @return {BasicForm} this
7349      */
7350     markInvalid : function(errors){
7351         if(errors instanceof Array){
7352             for(var i = 0, len = errors.length; i < len; i++){
7353                 var fieldError = errors[i];
7354                 var f = this.findField(fieldError.id);
7355                 if(f){
7356                     f.markInvalid(fieldError.msg);
7357                 }
7358             }
7359         }else{
7360             var field, id;
7361             for(id in errors){
7362                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7363                     field.markInvalid(errors[id]);
7364                 }
7365             }
7366         }
7367         //Roo.each(this.childForms || [], function (f) {
7368         //    f.markInvalid(errors);
7369         //});
7370         
7371         return this;
7372     },
7373
7374     /**
7375      * Set values for fields in this form in bulk.
7376      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7377      * @return {BasicForm} this
7378      */
7379     setValues : function(values){
7380         if(values instanceof Array){ // array of objects
7381             for(var i = 0, len = values.length; i < len; i++){
7382                 var v = values[i];
7383                 var f = this.findField(v.id);
7384                 if(f){
7385                     f.setValue(v.value);
7386                     if(this.trackResetOnLoad){
7387                         f.originalValue = f.getValue();
7388                     }
7389                 }
7390             }
7391         }else{ // object hash
7392             var field, id;
7393             for(id in values){
7394                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7395                     
7396                     if (field.setFromData && 
7397                         field.valueField && 
7398                         field.displayField &&
7399                         // combos' with local stores can 
7400                         // be queried via setValue()
7401                         // to set their value..
7402                         (field.store && !field.store.isLocal)
7403                         ) {
7404                         // it's a combo
7405                         var sd = { };
7406                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7407                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7408                         field.setFromData(sd);
7409                         
7410                     } else {
7411                         field.setValue(values[id]);
7412                     }
7413                     
7414                     
7415                     if(this.trackResetOnLoad){
7416                         field.originalValue = field.getValue();
7417                     }
7418                 }
7419             }
7420         }
7421          
7422         //Roo.each(this.childForms || [], function (f) {
7423         //    f.setValues(values);
7424         //});
7425                 
7426         return this;
7427     },
7428
7429     /**
7430      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7431      * they are returned as an array.
7432      * @param {Boolean} asString
7433      * @return {Object}
7434      */
7435     getValues : function(asString){
7436         //if (this.childForms) {
7437             // copy values from the child forms
7438         //    Roo.each(this.childForms, function (f) {
7439         //        this.setValues(f.getValues());
7440         //    }, this);
7441         //}
7442         
7443         
7444         
7445         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7446         if(asString === true){
7447             return fs;
7448         }
7449         return Roo.urlDecode(fs);
7450     },
7451     
7452     /**
7453      * Returns the fields in this form as an object with key/value pairs. 
7454      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7455      * @return {Object}
7456      */
7457     getFieldValues : function(with_hidden)
7458     {
7459         var items = this.getItems();
7460         var ret = {};
7461         items.each(function(f){
7462             if (!f.getName()) {
7463                 return;
7464             }
7465             var v = f.getValue();
7466             if (f.inputType =='radio') {
7467                 if (typeof(ret[f.getName()]) == 'undefined') {
7468                     ret[f.getName()] = ''; // empty..
7469                 }
7470                 
7471                 if (!f.el.dom.checked) {
7472                     return;
7473                     
7474                 }
7475                 v = f.el.dom.value;
7476                 
7477             }
7478             
7479             // not sure if this supported any more..
7480             if ((typeof(v) == 'object') && f.getRawValue) {
7481                 v = f.getRawValue() ; // dates..
7482             }
7483             // combo boxes where name != hiddenName...
7484             if (f.name != f.getName()) {
7485                 ret[f.name] = f.getRawValue();
7486             }
7487             ret[f.getName()] = v;
7488         });
7489         
7490         return ret;
7491     },
7492
7493     /**
7494      * Clears all invalid messages in this form.
7495      * @return {BasicForm} this
7496      */
7497     clearInvalid : function(){
7498         var items = this.getItems();
7499         
7500         items.each(function(f){
7501            f.clearInvalid();
7502         });
7503         
7504         
7505         
7506         return this;
7507     },
7508
7509     /**
7510      * Resets this form.
7511      * @return {BasicForm} this
7512      */
7513     reset : function(){
7514         var items = this.getItems();
7515         items.each(function(f){
7516             f.reset();
7517         });
7518         
7519         Roo.each(this.childForms || [], function (f) {
7520             f.reset();
7521         });
7522        
7523         
7524         return this;
7525     },
7526     getItems : function()
7527     {
7528         var r=new Roo.util.MixedCollection(false, function(o){
7529             return o.id || (o.id = Roo.id());
7530         });
7531         var iter = function(el) {
7532             if (el.inputEl) {
7533                 r.add(el);
7534             }
7535             if (!el.items) {
7536                 return;
7537             }
7538             Roo.each(el.items,function(e) {
7539                 iter(e);
7540             });
7541             
7542             
7543         };
7544         
7545         iter(this);
7546         return r;
7547         
7548         
7549         
7550         
7551     }
7552     
7553 });
7554
7555  
7556 /*
7557  * Based on:
7558  * Ext JS Library 1.1.1
7559  * Copyright(c) 2006-2007, Ext JS, LLC.
7560  *
7561  * Originally Released Under LGPL - original licence link has changed is not relivant.
7562  *
7563  * Fork - LGPL
7564  * <script type="text/javascript">
7565  */
7566 /**
7567  * @class Roo.form.VTypes
7568  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7569  * @singleton
7570  */
7571 Roo.form.VTypes = function(){
7572     // closure these in so they are only created once.
7573     var alpha = /^[a-zA-Z_]+$/;
7574     var alphanum = /^[a-zA-Z0-9_]+$/;
7575     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7576     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7577
7578     // All these messages and functions are configurable
7579     return {
7580         /**
7581          * The function used to validate email addresses
7582          * @param {String} value The email address
7583          */
7584         'email' : function(v){
7585             return email.test(v);
7586         },
7587         /**
7588          * The error text to display when the email validation function returns false
7589          * @type String
7590          */
7591         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7592         /**
7593          * The keystroke filter mask to be applied on email input
7594          * @type RegExp
7595          */
7596         'emailMask' : /[a-z0-9_\.\-@]/i,
7597
7598         /**
7599          * The function used to validate URLs
7600          * @param {String} value The URL
7601          */
7602         'url' : function(v){
7603             return url.test(v);
7604         },
7605         /**
7606          * The error text to display when the url validation function returns false
7607          * @type String
7608          */
7609         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7610         
7611         /**
7612          * The function used to validate alpha values
7613          * @param {String} value The value
7614          */
7615         'alpha' : function(v){
7616             return alpha.test(v);
7617         },
7618         /**
7619          * The error text to display when the alpha validation function returns false
7620          * @type String
7621          */
7622         'alphaText' : 'This field should only contain letters and _',
7623         /**
7624          * The keystroke filter mask to be applied on alpha input
7625          * @type RegExp
7626          */
7627         'alphaMask' : /[a-z_]/i,
7628
7629         /**
7630          * The function used to validate alphanumeric values
7631          * @param {String} value The value
7632          */
7633         'alphanum' : function(v){
7634             return alphanum.test(v);
7635         },
7636         /**
7637          * The error text to display when the alphanumeric validation function returns false
7638          * @type String
7639          */
7640         'alphanumText' : 'This field should only contain letters, numbers and _',
7641         /**
7642          * The keystroke filter mask to be applied on alphanumeric input
7643          * @type RegExp
7644          */
7645         'alphanumMask' : /[a-z0-9_]/i
7646     };
7647 }();/*
7648  * - LGPL
7649  *
7650  * Input
7651  * 
7652  */
7653
7654 /**
7655  * @class Roo.bootstrap.Input
7656  * @extends Roo.bootstrap.Component
7657  * Bootstrap Input class
7658  * @cfg {Boolean} disabled is it disabled
7659  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7660  * @cfg {String} name name of the input
7661  * @cfg {string} fieldLabel - the label associated
7662  * @cfg {string} placeholder - placeholder to put in text.
7663  * @cfg {string}  before - input group add on before
7664  * @cfg {string} after - input group add on after
7665  * @cfg {string} size - (lg|sm) or leave empty..
7666  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7667  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7668  * @cfg {Number} md colspan out of 12 for computer-sized screens
7669  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7670  * @cfg {string} value default value of the input
7671  * @cfg {Number} labelWidth set the width of label (0-12)
7672  * @cfg {String} labelAlign (top|left)
7673  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7674  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7675
7676  * @cfg {String} align (left|center|right) Default left
7677  * @cfg {Boolean} forceFeedback (true|false) Default false
7678  * 
7679  * 
7680  * 
7681  * 
7682  * @constructor
7683  * Create a new Input
7684  * @param {Object} config The config object
7685  */
7686
7687 Roo.bootstrap.Input = function(config){
7688     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7689    
7690         this.addEvents({
7691             /**
7692              * @event focus
7693              * Fires when this field receives input focus.
7694              * @param {Roo.form.Field} this
7695              */
7696             focus : true,
7697             /**
7698              * @event blur
7699              * Fires when this field loses input focus.
7700              * @param {Roo.form.Field} this
7701              */
7702             blur : true,
7703             /**
7704              * @event specialkey
7705              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7706              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7707              * @param {Roo.form.Field} this
7708              * @param {Roo.EventObject} e The event object
7709              */
7710             specialkey : true,
7711             /**
7712              * @event change
7713              * Fires just before the field blurs if the field value has changed.
7714              * @param {Roo.form.Field} this
7715              * @param {Mixed} newValue The new value
7716              * @param {Mixed} oldValue The original value
7717              */
7718             change : true,
7719             /**
7720              * @event invalid
7721              * Fires after the field has been marked as invalid.
7722              * @param {Roo.form.Field} this
7723              * @param {String} msg The validation message
7724              */
7725             invalid : true,
7726             /**
7727              * @event valid
7728              * Fires after the field has been validated with no errors.
7729              * @param {Roo.form.Field} this
7730              */
7731             valid : true,
7732              /**
7733              * @event keyup
7734              * Fires after the key up
7735              * @param {Roo.form.Field} this
7736              * @param {Roo.EventObject}  e The event Object
7737              */
7738             keyup : true
7739         });
7740 };
7741
7742 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7743      /**
7744      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7745       automatic validation (defaults to "keyup").
7746      */
7747     validationEvent : "keyup",
7748      /**
7749      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7750      */
7751     validateOnBlur : true,
7752     /**
7753      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7754      */
7755     validationDelay : 250,
7756      /**
7757      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7758      */
7759     focusClass : "x-form-focus",  // not needed???
7760     
7761        
7762     /**
7763      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7764      */
7765     invalidClass : "has-warning",
7766     
7767     /**
7768      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7769      */
7770     validClass : "has-success",
7771     
7772     /**
7773      * @cfg {Boolean} hasFeedback (true|false) default true
7774      */
7775     hasFeedback : true,
7776     
7777     /**
7778      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7779      */
7780     invalidFeedbackClass : "glyphicon-warning-sign",
7781     
7782     /**
7783      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7784      */
7785     validFeedbackClass : "glyphicon-ok",
7786     
7787     /**
7788      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7789      */
7790     selectOnFocus : false,
7791     
7792      /**
7793      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7794      */
7795     maskRe : null,
7796        /**
7797      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7798      */
7799     vtype : null,
7800     
7801       /**
7802      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7803      */
7804     disableKeyFilter : false,
7805     
7806        /**
7807      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7808      */
7809     disabled : false,
7810      /**
7811      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7812      */
7813     allowBlank : true,
7814     /**
7815      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7816      */
7817     blankText : "This field is required",
7818     
7819      /**
7820      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7821      */
7822     minLength : 0,
7823     /**
7824      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7825      */
7826     maxLength : Number.MAX_VALUE,
7827     /**
7828      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7829      */
7830     minLengthText : "The minimum length for this field is {0}",
7831     /**
7832      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7833      */
7834     maxLengthText : "The maximum length for this field is {0}",
7835   
7836     
7837     /**
7838      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7839      * If available, this function will be called only after the basic validators all return true, and will be passed the
7840      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7841      */
7842     validator : null,
7843     /**
7844      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7845      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7846      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7847      */
7848     regex : null,
7849     /**
7850      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7851      */
7852     regexText : "",
7853     
7854     autocomplete: false,
7855     
7856     
7857     fieldLabel : '',
7858     inputType : 'text',
7859     
7860     name : false,
7861     placeholder: false,
7862     before : false,
7863     after : false,
7864     size : false,
7865     hasFocus : false,
7866     preventMark: false,
7867     isFormField : true,
7868     value : '',
7869     labelWidth : 2,
7870     labelAlign : false,
7871     readOnly : false,
7872     align : false,
7873     formatedValue : false,
7874     forceFeedback : false,
7875     
7876     parentLabelAlign : function()
7877     {
7878         var parent = this;
7879         while (parent.parent()) {
7880             parent = parent.parent();
7881             if (typeof(parent.labelAlign) !='undefined') {
7882                 return parent.labelAlign;
7883             }
7884         }
7885         return 'left';
7886         
7887     },
7888     
7889     getAutoCreate : function(){
7890         
7891         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7892         
7893         var id = Roo.id();
7894         
7895         var cfg = {};
7896         
7897         if(this.inputType != 'hidden'){
7898             cfg.cls = 'form-group' //input-group
7899         }
7900         
7901         var input =  {
7902             tag: 'input',
7903             id : id,
7904             type : this.inputType,
7905             value : this.value,
7906             cls : 'form-control',
7907             placeholder : this.placeholder || '',
7908             autocomplete : this.autocomplete || 'new-password'
7909         };
7910         
7911         
7912         if(this.align){
7913             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7914         }
7915         
7916         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7917             input.maxLength = this.maxLength;
7918         }
7919         
7920         if (this.disabled) {
7921             input.disabled=true;
7922         }
7923         
7924         if (this.readOnly) {
7925             input.readonly=true;
7926         }
7927         
7928         if (this.name) {
7929             input.name = this.name;
7930         }
7931         if (this.size) {
7932             input.cls += ' input-' + this.size;
7933         }
7934         var settings=this;
7935         ['xs','sm','md','lg'].map(function(size){
7936             if (settings[size]) {
7937                 cfg.cls += ' col-' + size + '-' + settings[size];
7938             }
7939         });
7940         
7941         var inputblock = input;
7942         
7943         var feedback = {
7944             tag: 'span',
7945             cls: 'glyphicon form-control-feedback'
7946         };
7947             
7948         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7949             
7950             inputblock = {
7951                 cls : 'has-feedback',
7952                 cn :  [
7953                     input,
7954                     feedback
7955                 ] 
7956             };  
7957         }
7958         
7959         if (this.before || this.after) {
7960             
7961             inputblock = {
7962                 cls : 'input-group',
7963                 cn :  [] 
7964             };
7965             
7966             if (this.before && typeof(this.before) == 'string') {
7967                 
7968                 inputblock.cn.push({
7969                     tag :'span',
7970                     cls : 'roo-input-before input-group-addon',
7971                     html : this.before
7972                 });
7973             }
7974             if (this.before && typeof(this.before) == 'object') {
7975                 this.before = Roo.factory(this.before);
7976                 Roo.log(this.before);
7977                 inputblock.cn.push({
7978                     tag :'span',
7979                     cls : 'roo-input-before input-group-' +
7980                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7981                 });
7982             }
7983             
7984             inputblock.cn.push(input);
7985             
7986             if (this.after && typeof(this.after) == 'string') {
7987                 inputblock.cn.push({
7988                     tag :'span',
7989                     cls : 'roo-input-after input-group-addon',
7990                     html : this.after
7991                 });
7992             }
7993             if (this.after && typeof(this.after) == 'object') {
7994                 this.after = Roo.factory(this.after);
7995                 Roo.log(this.after);
7996                 inputblock.cn.push({
7997                     tag :'span',
7998                     cls : 'roo-input-after input-group-' +
7999                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8000                 });
8001             }
8002             
8003             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8004                 inputblock.cls += ' has-feedback';
8005                 inputblock.cn.push(feedback);
8006             }
8007         };
8008         
8009         if (align ==='left' && this.fieldLabel.length) {
8010                 Roo.log("left and has label");
8011                 cfg.cn = [
8012                     
8013                     {
8014                         tag: 'label',
8015                         'for' :  id,
8016                         cls : 'control-label col-sm-' + this.labelWidth,
8017                         html : this.fieldLabel
8018                         
8019                     },
8020                     {
8021                         cls : "col-sm-" + (12 - this.labelWidth), 
8022                         cn: [
8023                             inputblock
8024                         ]
8025                     }
8026                     
8027                 ];
8028         } else if ( this.fieldLabel.length) {
8029                 Roo.log(" label");
8030                  cfg.cn = [
8031                    
8032                     {
8033                         tag: 'label',
8034                         //cls : 'input-group-addon',
8035                         html : this.fieldLabel
8036                         
8037                     },
8038                     
8039                     inputblock
8040                     
8041                 ];
8042
8043         } else {
8044             
8045                 Roo.log(" no label && no align");
8046                 cfg.cn = [
8047                     
8048                         inputblock
8049                     
8050                 ];
8051                 
8052                 
8053         };
8054         Roo.log('input-parentType: ' + this.parentType);
8055         
8056         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8057            cfg.cls += ' navbar-form';
8058            Roo.log(cfg);
8059         }
8060         
8061         return cfg;
8062         
8063     },
8064     /**
8065      * return the real input element.
8066      */
8067     inputEl: function ()
8068     {
8069         return this.el.select('input.form-control',true).first();
8070     },
8071     
8072     tooltipEl : function()
8073     {
8074         return this.inputEl();
8075     },
8076     
8077     setDisabled : function(v)
8078     {
8079         var i  = this.inputEl().dom;
8080         if (!v) {
8081             i.removeAttribute('disabled');
8082             return;
8083             
8084         }
8085         i.setAttribute('disabled','true');
8086     },
8087     initEvents : function()
8088     {
8089           
8090         this.inputEl().on("keydown" , this.fireKey,  this);
8091         this.inputEl().on("focus", this.onFocus,  this);
8092         this.inputEl().on("blur", this.onBlur,  this);
8093         
8094         this.inputEl().relayEvent('keyup', this);
8095  
8096         // reference to original value for reset
8097         this.originalValue = this.getValue();
8098         //Roo.form.TextField.superclass.initEvents.call(this);
8099         if(this.validationEvent == 'keyup'){
8100             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8101             this.inputEl().on('keyup', this.filterValidation, this);
8102         }
8103         else if(this.validationEvent !== false){
8104             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8105         }
8106         
8107         if(this.selectOnFocus){
8108             this.on("focus", this.preFocus, this);
8109             
8110         }
8111         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8112             this.inputEl().on("keypress", this.filterKeys, this);
8113         }
8114        /* if(this.grow){
8115             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8116             this.el.on("click", this.autoSize,  this);
8117         }
8118         */
8119         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8120             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8121         }
8122         
8123         if (typeof(this.before) == 'object') {
8124             this.before.render(this.el.select('.roo-input-before',true).first());
8125         }
8126         if (typeof(this.after) == 'object') {
8127             this.after.render(this.el.select('.roo-input-after',true).first());
8128         }
8129         
8130         
8131     },
8132     filterValidation : function(e){
8133         if(!e.isNavKeyPress()){
8134             this.validationTask.delay(this.validationDelay);
8135         }
8136     },
8137      /**
8138      * Validates the field value
8139      * @return {Boolean} True if the value is valid, else false
8140      */
8141     validate : function(){
8142         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8143         if(this.disabled || this.validateValue(this.getRawValue())){
8144             this.markValid();
8145             return true;
8146         }
8147         
8148         this.markInvalid();
8149         return false;
8150     },
8151     
8152     
8153     /**
8154      * Validates a value according to the field's validation rules and marks the field as invalid
8155      * if the validation fails
8156      * @param {Mixed} value The value to validate
8157      * @return {Boolean} True if the value is valid, else false
8158      */
8159     validateValue : function(value){
8160         if(value.length < 1)  { // if it's blank
8161             if(this.allowBlank){
8162                 return true;
8163             }
8164             return false;
8165         }
8166         
8167         if(value.length < this.minLength){
8168             return false;
8169         }
8170         if(value.length > this.maxLength){
8171             return false;
8172         }
8173         if(this.vtype){
8174             var vt = Roo.form.VTypes;
8175             if(!vt[this.vtype](value, this)){
8176                 return false;
8177             }
8178         }
8179         if(typeof this.validator == "function"){
8180             var msg = this.validator(value);
8181             if(msg !== true){
8182                 return false;
8183             }
8184         }
8185         
8186         if(this.regex && !this.regex.test(value)){
8187             return false;
8188         }
8189         
8190         return true;
8191     },
8192
8193     
8194     
8195      // private
8196     fireKey : function(e){
8197         //Roo.log('field ' + e.getKey());
8198         if(e.isNavKeyPress()){
8199             this.fireEvent("specialkey", this, e);
8200         }
8201     },
8202     focus : function (selectText){
8203         if(this.rendered){
8204             this.inputEl().focus();
8205             if(selectText === true){
8206                 this.inputEl().dom.select();
8207             }
8208         }
8209         return this;
8210     } ,
8211     
8212     onFocus : function(){
8213         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8214            // this.el.addClass(this.focusClass);
8215         }
8216         if(!this.hasFocus){
8217             this.hasFocus = true;
8218             this.startValue = this.getValue();
8219             this.fireEvent("focus", this);
8220         }
8221     },
8222     
8223     beforeBlur : Roo.emptyFn,
8224
8225     
8226     // private
8227     onBlur : function(){
8228         this.beforeBlur();
8229         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8230             //this.el.removeClass(this.focusClass);
8231         }
8232         this.hasFocus = false;
8233         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8234             this.validate();
8235         }
8236         var v = this.getValue();
8237         if(String(v) !== String(this.startValue)){
8238             this.fireEvent('change', this, v, this.startValue);
8239         }
8240         this.fireEvent("blur", this);
8241     },
8242     
8243     /**
8244      * Resets the current field value to the originally loaded value and clears any validation messages
8245      */
8246     reset : function(){
8247         this.setValue(this.originalValue);
8248         this.validate();
8249     },
8250      /**
8251      * Returns the name of the field
8252      * @return {Mixed} name The name field
8253      */
8254     getName: function(){
8255         return this.name;
8256     },
8257      /**
8258      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8259      * @return {Mixed} value The field value
8260      */
8261     getValue : function(){
8262         
8263         var v = this.inputEl().getValue();
8264         
8265         return v;
8266     },
8267     /**
8268      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8269      * @return {Mixed} value The field value
8270      */
8271     getRawValue : function(){
8272         var v = this.inputEl().getValue();
8273         
8274         return v;
8275     },
8276     
8277     /**
8278      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8279      * @param {Mixed} value The value to set
8280      */
8281     setRawValue : function(v){
8282         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8283     },
8284     
8285     selectText : function(start, end){
8286         var v = this.getRawValue();
8287         if(v.length > 0){
8288             start = start === undefined ? 0 : start;
8289             end = end === undefined ? v.length : end;
8290             var d = this.inputEl().dom;
8291             if(d.setSelectionRange){
8292                 d.setSelectionRange(start, end);
8293             }else if(d.createTextRange){
8294                 var range = d.createTextRange();
8295                 range.moveStart("character", start);
8296                 range.moveEnd("character", v.length-end);
8297                 range.select();
8298             }
8299         }
8300     },
8301     
8302     /**
8303      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8304      * @param {Mixed} value The value to set
8305      */
8306     setValue : function(v){
8307         this.value = v;
8308         if(this.rendered){
8309             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8310             this.validate();
8311         }
8312     },
8313     
8314     /*
8315     processValue : function(value){
8316         if(this.stripCharsRe){
8317             var newValue = value.replace(this.stripCharsRe, '');
8318             if(newValue !== value){
8319                 this.setRawValue(newValue);
8320                 return newValue;
8321             }
8322         }
8323         return value;
8324     },
8325   */
8326     preFocus : function(){
8327         
8328         if(this.selectOnFocus){
8329             this.inputEl().dom.select();
8330         }
8331     },
8332     filterKeys : function(e){
8333         var k = e.getKey();
8334         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8335             return;
8336         }
8337         var c = e.getCharCode(), cc = String.fromCharCode(c);
8338         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8339             return;
8340         }
8341         if(!this.maskRe.test(cc)){
8342             e.stopEvent();
8343         }
8344     },
8345      /**
8346      * Clear any invalid styles/messages for this field
8347      */
8348     clearInvalid : function(){
8349         
8350         if(!this.el || this.preventMark){ // not rendered
8351             return;
8352         }
8353         this.el.removeClass(this.invalidClass);
8354         
8355         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8356             
8357             var feedback = this.el.select('.form-control-feedback', true).first();
8358             
8359             if(feedback){
8360                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8361             }
8362             
8363         }
8364         
8365         this.fireEvent('valid', this);
8366     },
8367     
8368      /**
8369      * Mark this field as valid
8370      */
8371     markValid : function()
8372     {
8373         if(!this.el  || this.preventMark){ // not rendered
8374             return;
8375         }
8376         
8377         this.el.removeClass([this.invalidClass, this.validClass]);
8378         
8379         var feedback = this.el.select('.form-control-feedback', true).first();
8380             
8381         if(feedback){
8382             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8383         }
8384
8385         if(this.disabled || this.allowBlank){
8386             return;
8387         }
8388         
8389         var formGroup = this.el.findParent('.form-group', false, true);
8390         
8391         if(formGroup){
8392             
8393             var label = formGroup.select('label', true).first();
8394             var icon = formGroup.select('i.fa-star', true).first();
8395             
8396             if(label && icon){
8397                 icon.remove();
8398             }
8399         }
8400         
8401         this.el.addClass(this.validClass);
8402         
8403         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8404             
8405             var feedback = this.el.select('.form-control-feedback', true).first();
8406             
8407             if(feedback){
8408                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8409                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8410             }
8411             
8412         }
8413         
8414         this.fireEvent('valid', this);
8415     },
8416     
8417      /**
8418      * Mark this field as invalid
8419      * @param {String} msg The validation message
8420      */
8421     markInvalid : function(msg)
8422     {
8423         if(!this.el  || this.preventMark){ // not rendered
8424             return;
8425         }
8426         
8427         this.el.removeClass([this.invalidClass, this.validClass]);
8428         
8429         var feedback = this.el.select('.form-control-feedback', true).first();
8430             
8431         if(feedback){
8432             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8433         }
8434
8435         if(this.disabled || this.allowBlank){
8436             return;
8437         }
8438         
8439         var formGroup = this.el.findParent('.form-group', false, true);
8440         
8441         if(formGroup){
8442             var label = formGroup.select('label', true).first();
8443             var icon = formGroup.select('i.fa-star', true).first();
8444
8445             if(!this.getValue().length && label && !icon){
8446                 this.el.findParent('.form-group', false, true).createChild({
8447                     tag : 'i',
8448                     cls : 'text-danger fa fa-lg fa-star',
8449                     tooltip : 'This field is required',
8450                     style : 'margin-right:5px;'
8451                 }, label, true);
8452             }
8453         }
8454         
8455         
8456         this.el.addClass(this.invalidClass);
8457         
8458         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8459             
8460             var feedback = this.el.select('.form-control-feedback', true).first();
8461             
8462             if(feedback){
8463                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8464                 
8465                 if(this.getValue().length || this.forceFeedback){
8466                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8467                 }
8468                 
8469             }
8470             
8471         }
8472         
8473         this.fireEvent('invalid', this, msg);
8474     },
8475     // private
8476     SafariOnKeyDown : function(event)
8477     {
8478         // this is a workaround for a password hang bug on chrome/ webkit.
8479         
8480         var isSelectAll = false;
8481         
8482         if(this.inputEl().dom.selectionEnd > 0){
8483             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8484         }
8485         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8486             event.preventDefault();
8487             this.setValue('');
8488             return;
8489         }
8490         
8491         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8492             
8493             event.preventDefault();
8494             // this is very hacky as keydown always get's upper case.
8495             //
8496             var cc = String.fromCharCode(event.getCharCode());
8497             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8498             
8499         }
8500     },
8501     adjustWidth : function(tag, w){
8502         tag = tag.toLowerCase();
8503         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8504             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8505                 if(tag == 'input'){
8506                     return w + 2;
8507                 }
8508                 if(tag == 'textarea'){
8509                     return w-2;
8510                 }
8511             }else if(Roo.isOpera){
8512                 if(tag == 'input'){
8513                     return w + 2;
8514                 }
8515                 if(tag == 'textarea'){
8516                     return w-2;
8517                 }
8518             }
8519         }
8520         return w;
8521     }
8522     
8523 });
8524
8525  
8526 /*
8527  * - LGPL
8528  *
8529  * Input
8530  * 
8531  */
8532
8533 /**
8534  * @class Roo.bootstrap.TextArea
8535  * @extends Roo.bootstrap.Input
8536  * Bootstrap TextArea class
8537  * @cfg {Number} cols Specifies the visible width of a text area
8538  * @cfg {Number} rows Specifies the visible number of lines in a text area
8539  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8540  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8541  * @cfg {string} html text
8542  * 
8543  * @constructor
8544  * Create a new TextArea
8545  * @param {Object} config The config object
8546  */
8547
8548 Roo.bootstrap.TextArea = function(config){
8549     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8550    
8551 };
8552
8553 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8554      
8555     cols : false,
8556     rows : 5,
8557     readOnly : false,
8558     warp : 'soft',
8559     resize : false,
8560     value: false,
8561     html: false,
8562     
8563     getAutoCreate : function(){
8564         
8565         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8566         
8567         var id = Roo.id();
8568         
8569         var cfg = {};
8570         
8571         var input =  {
8572             tag: 'textarea',
8573             id : id,
8574             warp : this.warp,
8575             rows : this.rows,
8576             value : this.value || '',
8577             html: this.html || '',
8578             cls : 'form-control',
8579             placeholder : this.placeholder || '' 
8580             
8581         };
8582         
8583         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8584             input.maxLength = this.maxLength;
8585         }
8586         
8587         if(this.resize){
8588             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8589         }
8590         
8591         if(this.cols){
8592             input.cols = this.cols;
8593         }
8594         
8595         if (this.readOnly) {
8596             input.readonly = true;
8597         }
8598         
8599         if (this.name) {
8600             input.name = this.name;
8601         }
8602         
8603         if (this.size) {
8604             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8605         }
8606         
8607         var settings=this;
8608         ['xs','sm','md','lg'].map(function(size){
8609             if (settings[size]) {
8610                 cfg.cls += ' col-' + size + '-' + settings[size];
8611             }
8612         });
8613         
8614         var inputblock = input;
8615         
8616         if(this.hasFeedback && !this.allowBlank){
8617             
8618             var feedback = {
8619                 tag: 'span',
8620                 cls: 'glyphicon form-control-feedback'
8621             };
8622
8623             inputblock = {
8624                 cls : 'has-feedback',
8625                 cn :  [
8626                     input,
8627                     feedback
8628                 ] 
8629             };  
8630         }
8631         
8632         
8633         if (this.before || this.after) {
8634             
8635             inputblock = {
8636                 cls : 'input-group',
8637                 cn :  [] 
8638             };
8639             if (this.before) {
8640                 inputblock.cn.push({
8641                     tag :'span',
8642                     cls : 'input-group-addon',
8643                     html : this.before
8644                 });
8645             }
8646             
8647             inputblock.cn.push(input);
8648             
8649             if(this.hasFeedback && !this.allowBlank){
8650                 inputblock.cls += ' has-feedback';
8651                 inputblock.cn.push(feedback);
8652             }
8653             
8654             if (this.after) {
8655                 inputblock.cn.push({
8656                     tag :'span',
8657                     cls : 'input-group-addon',
8658                     html : this.after
8659                 });
8660             }
8661             
8662         }
8663         
8664         if (align ==='left' && this.fieldLabel.length) {
8665                 Roo.log("left and has label");
8666                 cfg.cn = [
8667                     
8668                     {
8669                         tag: 'label',
8670                         'for' :  id,
8671                         cls : 'control-label col-sm-' + this.labelWidth,
8672                         html : this.fieldLabel
8673                         
8674                     },
8675                     {
8676                         cls : "col-sm-" + (12 - this.labelWidth), 
8677                         cn: [
8678                             inputblock
8679                         ]
8680                     }
8681                     
8682                 ];
8683         } else if ( this.fieldLabel.length) {
8684                 Roo.log(" label");
8685                  cfg.cn = [
8686                    
8687                     {
8688                         tag: 'label',
8689                         //cls : 'input-group-addon',
8690                         html : this.fieldLabel
8691                         
8692                     },
8693                     
8694                     inputblock
8695                     
8696                 ];
8697
8698         } else {
8699             
8700                    Roo.log(" no label && no align");
8701                 cfg.cn = [
8702                     
8703                         inputblock
8704                     
8705                 ];
8706                 
8707                 
8708         }
8709         
8710         if (this.disabled) {
8711             input.disabled=true;
8712         }
8713         
8714         return cfg;
8715         
8716     },
8717     /**
8718      * return the real textarea element.
8719      */
8720     inputEl: function ()
8721     {
8722         return this.el.select('textarea.form-control',true).first();
8723     },
8724     
8725     /**
8726      * Clear any invalid styles/messages for this field
8727      */
8728     clearInvalid : function()
8729     {
8730         
8731         if(!this.el || this.preventMark){ // not rendered
8732             return;
8733         }
8734         
8735         var label = this.el.select('label', true).first();
8736         var icon = this.el.select('i.fa-star', true).first();
8737         
8738         if(label && icon){
8739             icon.remove();
8740         }
8741         
8742         this.el.removeClass(this.invalidClass);
8743         
8744         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8745             
8746             var feedback = this.el.select('.form-control-feedback', true).first();
8747             
8748             if(feedback){
8749                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8750             }
8751             
8752         }
8753         
8754         this.fireEvent('valid', this);
8755     },
8756     
8757      /**
8758      * Mark this field as valid
8759      */
8760     markValid : function()
8761     {
8762         if(!this.el  || this.preventMark){ // not rendered
8763             return;
8764         }
8765         
8766         this.el.removeClass([this.invalidClass, this.validClass]);
8767         
8768         var feedback = this.el.select('.form-control-feedback', true).first();
8769             
8770         if(feedback){
8771             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8772         }
8773
8774         if(this.disabled || this.allowBlank){
8775             return;
8776         }
8777         
8778         var label = this.el.select('label', true).first();
8779         var icon = this.el.select('i.fa-star', true).first();
8780         
8781         if(label && icon){
8782             icon.remove();
8783         }
8784         
8785         this.el.addClass(this.validClass);
8786         
8787         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8788             
8789             var feedback = this.el.select('.form-control-feedback', true).first();
8790             
8791             if(feedback){
8792                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8793                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8794             }
8795             
8796         }
8797         
8798         this.fireEvent('valid', this);
8799     },
8800     
8801      /**
8802      * Mark this field as invalid
8803      * @param {String} msg The validation message
8804      */
8805     markInvalid : function(msg)
8806     {
8807         if(!this.el  || this.preventMark){ // not rendered
8808             return;
8809         }
8810         
8811         this.el.removeClass([this.invalidClass, this.validClass]);
8812         
8813         var feedback = this.el.select('.form-control-feedback', true).first();
8814             
8815         if(feedback){
8816             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8817         }
8818
8819         if(this.disabled || this.allowBlank){
8820             return;
8821         }
8822         
8823         var label = this.el.select('label', true).first();
8824         var icon = this.el.select('i.fa-star', true).first();
8825         
8826         if(!this.getValue().length && label && !icon){
8827             this.el.createChild({
8828                 tag : 'i',
8829                 cls : 'text-danger fa fa-lg fa-star',
8830                 tooltip : 'This field is required',
8831                 style : 'margin-right:5px;'
8832             }, label, true);
8833         }
8834
8835         this.el.addClass(this.invalidClass);
8836         
8837         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8838             
8839             var feedback = this.el.select('.form-control-feedback', true).first();
8840             
8841             if(feedback){
8842                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8843                 
8844                 if(this.getValue().length || this.forceFeedback){
8845                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8846                 }
8847                 
8848             }
8849             
8850         }
8851         
8852         this.fireEvent('invalid', this, msg);
8853     }
8854 });
8855
8856  
8857 /*
8858  * - LGPL
8859  *
8860  * trigger field - base class for combo..
8861  * 
8862  */
8863  
8864 /**
8865  * @class Roo.bootstrap.TriggerField
8866  * @extends Roo.bootstrap.Input
8867  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8868  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8869  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8870  * for which you can provide a custom implementation.  For example:
8871  * <pre><code>
8872 var trigger = new Roo.bootstrap.TriggerField();
8873 trigger.onTriggerClick = myTriggerFn;
8874 trigger.applyTo('my-field');
8875 </code></pre>
8876  *
8877  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8878  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8879  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8880  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8881  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8882
8883  * @constructor
8884  * Create a new TriggerField.
8885  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8886  * to the base TextField)
8887  */
8888 Roo.bootstrap.TriggerField = function(config){
8889     this.mimicing = false;
8890     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8891 };
8892
8893 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8894     /**
8895      * @cfg {String} triggerClass A CSS class to apply to the trigger
8896      */
8897      /**
8898      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8899      */
8900     hideTrigger:false,
8901
8902     /**
8903      * @cfg {Boolean} removable (true|false) special filter default false
8904      */
8905     removable : false,
8906     
8907     /** @cfg {Boolean} grow @hide */
8908     /** @cfg {Number} growMin @hide */
8909     /** @cfg {Number} growMax @hide */
8910
8911     /**
8912      * @hide 
8913      * @method
8914      */
8915     autoSize: Roo.emptyFn,
8916     // private
8917     monitorTab : true,
8918     // private
8919     deferHeight : true,
8920
8921     
8922     actionMode : 'wrap',
8923     
8924     caret : false,
8925     
8926     
8927     getAutoCreate : function(){
8928        
8929         var align = this.labelAlign || this.parentLabelAlign();
8930         
8931         var id = Roo.id();
8932         
8933         var cfg = {
8934             cls: 'form-group' //input-group
8935         };
8936         
8937         
8938         var input =  {
8939             tag: 'input',
8940             id : id,
8941             type : this.inputType,
8942             cls : 'form-control',
8943             autocomplete: 'new-password',
8944             placeholder : this.placeholder || '' 
8945             
8946         };
8947         if (this.name) {
8948             input.name = this.name;
8949         }
8950         if (this.size) {
8951             input.cls += ' input-' + this.size;
8952         }
8953         
8954         if (this.disabled) {
8955             input.disabled=true;
8956         }
8957         
8958         var inputblock = input;
8959         
8960         if(this.hasFeedback && !this.allowBlank){
8961             
8962             var feedback = {
8963                 tag: 'span',
8964                 cls: 'glyphicon form-control-feedback'
8965             };
8966             
8967             if(this.removable && !this.editable && !this.tickable){
8968                 inputblock = {
8969                     cls : 'has-feedback',
8970                     cn :  [
8971                         inputblock,
8972                         {
8973                             tag: 'button',
8974                             html : 'x',
8975                             cls : 'roo-combo-removable-btn close'
8976                         },
8977                         feedback
8978                     ] 
8979                 };
8980             } else {
8981                 inputblock = {
8982                     cls : 'has-feedback',
8983                     cn :  [
8984                         inputblock,
8985                         feedback
8986                     ] 
8987                 };
8988             }
8989
8990         } else {
8991             if(this.removable && !this.editable && !this.tickable){
8992                 inputblock = {
8993                     cls : 'roo-removable',
8994                     cn :  [
8995                         inputblock,
8996                         {
8997                             tag: 'button',
8998                             html : 'x',
8999                             cls : 'roo-combo-removable-btn close'
9000                         }
9001                     ] 
9002                 };
9003             }
9004         }
9005         
9006         if (this.before || this.after) {
9007             
9008             inputblock = {
9009                 cls : 'input-group',
9010                 cn :  [] 
9011             };
9012             if (this.before) {
9013                 inputblock.cn.push({
9014                     tag :'span',
9015                     cls : 'input-group-addon',
9016                     html : this.before
9017                 });
9018             }
9019             
9020             inputblock.cn.push(input);
9021             
9022             if(this.hasFeedback && !this.allowBlank){
9023                 inputblock.cls += ' has-feedback';
9024                 inputblock.cn.push(feedback);
9025             }
9026             
9027             if (this.after) {
9028                 inputblock.cn.push({
9029                     tag :'span',
9030                     cls : 'input-group-addon',
9031                     html : this.after
9032                 });
9033             }
9034             
9035         };
9036         
9037         var box = {
9038             tag: 'div',
9039             cn: [
9040                 {
9041                     tag: 'input',
9042                     type : 'hidden',
9043                     cls: 'form-hidden-field'
9044                 },
9045                 inputblock
9046             ]
9047             
9048         };
9049         
9050         if(this.multiple){
9051             Roo.log('multiple');
9052             
9053             box = {
9054                 tag: 'div',
9055                 cn: [
9056                     {
9057                         tag: 'input',
9058                         type : 'hidden',
9059                         cls: 'form-hidden-field'
9060                     },
9061                     {
9062                         tag: 'ul',
9063                         cls: 'select2-choices',
9064                         cn:[
9065                             {
9066                                 tag: 'li',
9067                                 cls: 'select2-search-field',
9068                                 cn: [
9069
9070                                     inputblock
9071                                 ]
9072                             }
9073                         ]
9074                     }
9075                 ]
9076             }
9077         };
9078         
9079         var combobox = {
9080             cls: 'select2-container input-group',
9081             cn: [
9082                 box
9083 //                {
9084 //                    tag: 'ul',
9085 //                    cls: 'typeahead typeahead-long dropdown-menu',
9086 //                    style: 'display:none'
9087 //                }
9088             ]
9089         };
9090         
9091         if(!this.multiple && this.showToggleBtn){
9092             
9093             var caret = {
9094                         tag: 'span',
9095                         cls: 'caret'
9096              };
9097             if (this.caret != false) {
9098                 caret = {
9099                      tag: 'i',
9100                      cls: 'fa fa-' + this.caret
9101                 };
9102                 
9103             }
9104             
9105             combobox.cn.push({
9106                 tag :'span',
9107                 cls : 'input-group-addon btn dropdown-toggle',
9108                 cn : [
9109                     caret,
9110                     {
9111                         tag: 'span',
9112                         cls: 'combobox-clear',
9113                         cn  : [
9114                             {
9115                                 tag : 'i',
9116                                 cls: 'icon-remove'
9117                             }
9118                         ]
9119                     }
9120                 ]
9121
9122             })
9123         }
9124         
9125         if(this.multiple){
9126             combobox.cls += ' select2-container-multi';
9127         }
9128         
9129         if (align ==='left' && this.fieldLabel.length) {
9130             
9131                 Roo.log("left and has label");
9132                 cfg.cn = [
9133                     
9134                     {
9135                         tag: 'label',
9136                         'for' :  id,
9137                         cls : 'control-label col-sm-' + this.labelWidth,
9138                         html : this.fieldLabel
9139                         
9140                     },
9141                     {
9142                         cls : "col-sm-" + (12 - this.labelWidth), 
9143                         cn: [
9144                             combobox
9145                         ]
9146                     }
9147                     
9148                 ];
9149         } else if ( this.fieldLabel.length) {
9150                 Roo.log(" label");
9151                  cfg.cn = [
9152                    
9153                     {
9154                         tag: 'label',
9155                         //cls : 'input-group-addon',
9156                         html : this.fieldLabel
9157                         
9158                     },
9159                     
9160                     combobox
9161                     
9162                 ];
9163
9164         } else {
9165             
9166                 Roo.log(" no label && no align");
9167                 cfg = combobox
9168                      
9169                 
9170         }
9171          
9172         var settings=this;
9173         ['xs','sm','md','lg'].map(function(size){
9174             if (settings[size]) {
9175                 cfg.cls += ' col-' + size + '-' + settings[size];
9176             }
9177         });
9178         Roo.log(cfg);
9179         return cfg;
9180         
9181     },
9182     
9183     
9184     
9185     // private
9186     onResize : function(w, h){
9187 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9188 //        if(typeof w == 'number'){
9189 //            var x = w - this.trigger.getWidth();
9190 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9191 //            this.trigger.setStyle('left', x+'px');
9192 //        }
9193     },
9194
9195     // private
9196     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9197
9198     // private
9199     getResizeEl : function(){
9200         return this.inputEl();
9201     },
9202
9203     // private
9204     getPositionEl : function(){
9205         return this.inputEl();
9206     },
9207
9208     // private
9209     alignErrorIcon : function(){
9210         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9211     },
9212
9213     // private
9214     initEvents : function(){
9215         
9216         this.createList();
9217         
9218         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9219         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9220         if(!this.multiple && this.showToggleBtn){
9221             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9222             if(this.hideTrigger){
9223                 this.trigger.setDisplayed(false);
9224             }
9225             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9226         }
9227         
9228         if(this.multiple){
9229             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9230         }
9231         
9232         if(this.removable && !this.editable && !this.tickable){
9233             var close = this.closeTriggerEl();
9234             
9235             if(close){
9236                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9237                 close.on('click', this.removeBtnClick, this, close);
9238             }
9239         }
9240         
9241         //this.trigger.addClassOnOver('x-form-trigger-over');
9242         //this.trigger.addClassOnClick('x-form-trigger-click');
9243         
9244         //if(!this.width){
9245         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9246         //}
9247     },
9248     
9249     closeTriggerEl : function()
9250     {
9251         var close = this.el.select('.roo-combo-removable-btn', true).first();
9252         return close ? close : false;
9253     },
9254     
9255     removeBtnClick : function(e, h, el)
9256     {
9257         e.preventDefault();
9258         
9259         if(this.fireEvent("remove", this) !== false){
9260             this.reset();
9261         }
9262     },
9263     
9264     createList : function()
9265     {
9266         this.list = Roo.get(document.body).createChild({
9267             tag: 'ul',
9268             cls: 'typeahead typeahead-long dropdown-menu',
9269             style: 'display:none'
9270         });
9271         
9272         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9273         
9274     },
9275
9276     // private
9277     initTrigger : function(){
9278        
9279     },
9280
9281     // private
9282     onDestroy : function(){
9283         if(this.trigger){
9284             this.trigger.removeAllListeners();
9285           //  this.trigger.remove();
9286         }
9287         //if(this.wrap){
9288         //    this.wrap.remove();
9289         //}
9290         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9291     },
9292
9293     // private
9294     onFocus : function(){
9295         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9296         /*
9297         if(!this.mimicing){
9298             this.wrap.addClass('x-trigger-wrap-focus');
9299             this.mimicing = true;
9300             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9301             if(this.monitorTab){
9302                 this.el.on("keydown", this.checkTab, this);
9303             }
9304         }
9305         */
9306     },
9307
9308     // private
9309     checkTab : function(e){
9310         if(e.getKey() == e.TAB){
9311             this.triggerBlur();
9312         }
9313     },
9314
9315     // private
9316     onBlur : function(){
9317         // do nothing
9318     },
9319
9320     // private
9321     mimicBlur : function(e, t){
9322         /*
9323         if(!this.wrap.contains(t) && this.validateBlur()){
9324             this.triggerBlur();
9325         }
9326         */
9327     },
9328
9329     // private
9330     triggerBlur : function(){
9331         this.mimicing = false;
9332         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9333         if(this.monitorTab){
9334             this.el.un("keydown", this.checkTab, this);
9335         }
9336         //this.wrap.removeClass('x-trigger-wrap-focus');
9337         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9338     },
9339
9340     // private
9341     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9342     validateBlur : function(e, t){
9343         return true;
9344     },
9345
9346     // private
9347     onDisable : function(){
9348         this.inputEl().dom.disabled = true;
9349         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9350         //if(this.wrap){
9351         //    this.wrap.addClass('x-item-disabled');
9352         //}
9353     },
9354
9355     // private
9356     onEnable : function(){
9357         this.inputEl().dom.disabled = false;
9358         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9359         //if(this.wrap){
9360         //    this.el.removeClass('x-item-disabled');
9361         //}
9362     },
9363
9364     // private
9365     onShow : function(){
9366         var ae = this.getActionEl();
9367         
9368         if(ae){
9369             ae.dom.style.display = '';
9370             ae.dom.style.visibility = 'visible';
9371         }
9372     },
9373
9374     // private
9375     
9376     onHide : function(){
9377         var ae = this.getActionEl();
9378         ae.dom.style.display = 'none';
9379     },
9380
9381     /**
9382      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9383      * by an implementing function.
9384      * @method
9385      * @param {EventObject} e
9386      */
9387     onTriggerClick : Roo.emptyFn
9388 });
9389  /*
9390  * Based on:
9391  * Ext JS Library 1.1.1
9392  * Copyright(c) 2006-2007, Ext JS, LLC.
9393  *
9394  * Originally Released Under LGPL - original licence link has changed is not relivant.
9395  *
9396  * Fork - LGPL
9397  * <script type="text/javascript">
9398  */
9399
9400
9401 /**
9402  * @class Roo.data.SortTypes
9403  * @singleton
9404  * Defines the default sorting (casting?) comparison functions used when sorting data.
9405  */
9406 Roo.data.SortTypes = {
9407     /**
9408      * Default sort that does nothing
9409      * @param {Mixed} s The value being converted
9410      * @return {Mixed} The comparison value
9411      */
9412     none : function(s){
9413         return s;
9414     },
9415     
9416     /**
9417      * The regular expression used to strip tags
9418      * @type {RegExp}
9419      * @property
9420      */
9421     stripTagsRE : /<\/?[^>]+>/gi,
9422     
9423     /**
9424      * Strips all HTML tags to sort on text only
9425      * @param {Mixed} s The value being converted
9426      * @return {String} The comparison value
9427      */
9428     asText : function(s){
9429         return String(s).replace(this.stripTagsRE, "");
9430     },
9431     
9432     /**
9433      * Strips all HTML tags to sort on text only - Case insensitive
9434      * @param {Mixed} s The value being converted
9435      * @return {String} The comparison value
9436      */
9437     asUCText : function(s){
9438         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9439     },
9440     
9441     /**
9442      * Case insensitive string
9443      * @param {Mixed} s The value being converted
9444      * @return {String} The comparison value
9445      */
9446     asUCString : function(s) {
9447         return String(s).toUpperCase();
9448     },
9449     
9450     /**
9451      * Date sorting
9452      * @param {Mixed} s The value being converted
9453      * @return {Number} The comparison value
9454      */
9455     asDate : function(s) {
9456         if(!s){
9457             return 0;
9458         }
9459         if(s instanceof Date){
9460             return s.getTime();
9461         }
9462         return Date.parse(String(s));
9463     },
9464     
9465     /**
9466      * Float sorting
9467      * @param {Mixed} s The value being converted
9468      * @return {Float} The comparison value
9469      */
9470     asFloat : function(s) {
9471         var val = parseFloat(String(s).replace(/,/g, ""));
9472         if(isNaN(val)) {
9473             val = 0;
9474         }
9475         return val;
9476     },
9477     
9478     /**
9479      * Integer sorting
9480      * @param {Mixed} s The value being converted
9481      * @return {Number} The comparison value
9482      */
9483     asInt : function(s) {
9484         var val = parseInt(String(s).replace(/,/g, ""));
9485         if(isNaN(val)) {
9486             val = 0;
9487         }
9488         return val;
9489     }
9490 };/*
9491  * Based on:
9492  * Ext JS Library 1.1.1
9493  * Copyright(c) 2006-2007, Ext JS, LLC.
9494  *
9495  * Originally Released Under LGPL - original licence link has changed is not relivant.
9496  *
9497  * Fork - LGPL
9498  * <script type="text/javascript">
9499  */
9500
9501 /**
9502 * @class Roo.data.Record
9503  * Instances of this class encapsulate both record <em>definition</em> information, and record
9504  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9505  * to access Records cached in an {@link Roo.data.Store} object.<br>
9506  * <p>
9507  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9508  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9509  * objects.<br>
9510  * <p>
9511  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9512  * @constructor
9513  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9514  * {@link #create}. The parameters are the same.
9515  * @param {Array} data An associative Array of data values keyed by the field name.
9516  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9517  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9518  * not specified an integer id is generated.
9519  */
9520 Roo.data.Record = function(data, id){
9521     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9522     this.data = data;
9523 };
9524
9525 /**
9526  * Generate a constructor for a specific record layout.
9527  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9528  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9529  * Each field definition object may contain the following properties: <ul>
9530  * <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,
9531  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9532  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9533  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9534  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9535  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9536  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9537  * this may be omitted.</p></li>
9538  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9539  * <ul><li>auto (Default, implies no conversion)</li>
9540  * <li>string</li>
9541  * <li>int</li>
9542  * <li>float</li>
9543  * <li>boolean</li>
9544  * <li>date</li></ul></p></li>
9545  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9546  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9547  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9548  * by the Reader into an object that will be stored in the Record. It is passed the
9549  * following parameters:<ul>
9550  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9551  * </ul></p></li>
9552  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9553  * </ul>
9554  * <br>usage:<br><pre><code>
9555 var TopicRecord = Roo.data.Record.create(
9556     {name: 'title', mapping: 'topic_title'},
9557     {name: 'author', mapping: 'username'},
9558     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9559     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9560     {name: 'lastPoster', mapping: 'user2'},
9561     {name: 'excerpt', mapping: 'post_text'}
9562 );
9563
9564 var myNewRecord = new TopicRecord({
9565     title: 'Do my job please',
9566     author: 'noobie',
9567     totalPosts: 1,
9568     lastPost: new Date(),
9569     lastPoster: 'Animal',
9570     excerpt: 'No way dude!'
9571 });
9572 myStore.add(myNewRecord);
9573 </code></pre>
9574  * @method create
9575  * @static
9576  */
9577 Roo.data.Record.create = function(o){
9578     var f = function(){
9579         f.superclass.constructor.apply(this, arguments);
9580     };
9581     Roo.extend(f, Roo.data.Record);
9582     var p = f.prototype;
9583     p.fields = new Roo.util.MixedCollection(false, function(field){
9584         return field.name;
9585     });
9586     for(var i = 0, len = o.length; i < len; i++){
9587         p.fields.add(new Roo.data.Field(o[i]));
9588     }
9589     f.getField = function(name){
9590         return p.fields.get(name);  
9591     };
9592     return f;
9593 };
9594
9595 Roo.data.Record.AUTO_ID = 1000;
9596 Roo.data.Record.EDIT = 'edit';
9597 Roo.data.Record.REJECT = 'reject';
9598 Roo.data.Record.COMMIT = 'commit';
9599
9600 Roo.data.Record.prototype = {
9601     /**
9602      * Readonly flag - true if this record has been modified.
9603      * @type Boolean
9604      */
9605     dirty : false,
9606     editing : false,
9607     error: null,
9608     modified: null,
9609
9610     // private
9611     join : function(store){
9612         this.store = store;
9613     },
9614
9615     /**
9616      * Set the named field to the specified value.
9617      * @param {String} name The name of the field to set.
9618      * @param {Object} value The value to set the field to.
9619      */
9620     set : function(name, value){
9621         if(this.data[name] == value){
9622             return;
9623         }
9624         this.dirty = true;
9625         if(!this.modified){
9626             this.modified = {};
9627         }
9628         if(typeof this.modified[name] == 'undefined'){
9629             this.modified[name] = this.data[name];
9630         }
9631         this.data[name] = value;
9632         if(!this.editing && this.store){
9633             this.store.afterEdit(this);
9634         }       
9635     },
9636
9637     /**
9638      * Get the value of the named field.
9639      * @param {String} name The name of the field to get the value of.
9640      * @return {Object} The value of the field.
9641      */
9642     get : function(name){
9643         return this.data[name]; 
9644     },
9645
9646     // private
9647     beginEdit : function(){
9648         this.editing = true;
9649         this.modified = {}; 
9650     },
9651
9652     // private
9653     cancelEdit : function(){
9654         this.editing = false;
9655         delete this.modified;
9656     },
9657
9658     // private
9659     endEdit : function(){
9660         this.editing = false;
9661         if(this.dirty && this.store){
9662             this.store.afterEdit(this);
9663         }
9664     },
9665
9666     /**
9667      * Usually called by the {@link Roo.data.Store} which owns the Record.
9668      * Rejects all changes made to the Record since either creation, or the last commit operation.
9669      * Modified fields are reverted to their original values.
9670      * <p>
9671      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9672      * of reject operations.
9673      */
9674     reject : function(){
9675         var m = this.modified;
9676         for(var n in m){
9677             if(typeof m[n] != "function"){
9678                 this.data[n] = m[n];
9679             }
9680         }
9681         this.dirty = false;
9682         delete this.modified;
9683         this.editing = false;
9684         if(this.store){
9685             this.store.afterReject(this);
9686         }
9687     },
9688
9689     /**
9690      * Usually called by the {@link Roo.data.Store} which owns the Record.
9691      * Commits all changes made to the Record since either creation, or the last commit operation.
9692      * <p>
9693      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9694      * of commit operations.
9695      */
9696     commit : function(){
9697         this.dirty = false;
9698         delete this.modified;
9699         this.editing = false;
9700         if(this.store){
9701             this.store.afterCommit(this);
9702         }
9703     },
9704
9705     // private
9706     hasError : function(){
9707         return this.error != null;
9708     },
9709
9710     // private
9711     clearError : function(){
9712         this.error = null;
9713     },
9714
9715     /**
9716      * Creates a copy of this record.
9717      * @param {String} id (optional) A new record id if you don't want to use this record's id
9718      * @return {Record}
9719      */
9720     copy : function(newId) {
9721         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9722     }
9723 };/*
9724  * Based on:
9725  * Ext JS Library 1.1.1
9726  * Copyright(c) 2006-2007, Ext JS, LLC.
9727  *
9728  * Originally Released Under LGPL - original licence link has changed is not relivant.
9729  *
9730  * Fork - LGPL
9731  * <script type="text/javascript">
9732  */
9733
9734
9735
9736 /**
9737  * @class Roo.data.Store
9738  * @extends Roo.util.Observable
9739  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9740  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9741  * <p>
9742  * 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
9743  * has no knowledge of the format of the data returned by the Proxy.<br>
9744  * <p>
9745  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9746  * instances from the data object. These records are cached and made available through accessor functions.
9747  * @constructor
9748  * Creates a new Store.
9749  * @param {Object} config A config object containing the objects needed for the Store to access data,
9750  * and read the data into Records.
9751  */
9752 Roo.data.Store = function(config){
9753     this.data = new Roo.util.MixedCollection(false);
9754     this.data.getKey = function(o){
9755         return o.id;
9756     };
9757     this.baseParams = {};
9758     // private
9759     this.paramNames = {
9760         "start" : "start",
9761         "limit" : "limit",
9762         "sort" : "sort",
9763         "dir" : "dir",
9764         "multisort" : "_multisort"
9765     };
9766
9767     if(config && config.data){
9768         this.inlineData = config.data;
9769         delete config.data;
9770     }
9771
9772     Roo.apply(this, config);
9773     
9774     if(this.reader){ // reader passed
9775         this.reader = Roo.factory(this.reader, Roo.data);
9776         this.reader.xmodule = this.xmodule || false;
9777         if(!this.recordType){
9778             this.recordType = this.reader.recordType;
9779         }
9780         if(this.reader.onMetaChange){
9781             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9782         }
9783     }
9784
9785     if(this.recordType){
9786         this.fields = this.recordType.prototype.fields;
9787     }
9788     this.modified = [];
9789
9790     this.addEvents({
9791         /**
9792          * @event datachanged
9793          * Fires when the data cache has changed, and a widget which is using this Store
9794          * as a Record cache should refresh its view.
9795          * @param {Store} this
9796          */
9797         datachanged : true,
9798         /**
9799          * @event metachange
9800          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9801          * @param {Store} this
9802          * @param {Object} meta The JSON metadata
9803          */
9804         metachange : true,
9805         /**
9806          * @event add
9807          * Fires when Records have been added to the Store
9808          * @param {Store} this
9809          * @param {Roo.data.Record[]} records The array of Records added
9810          * @param {Number} index The index at which the record(s) were added
9811          */
9812         add : true,
9813         /**
9814          * @event remove
9815          * Fires when a Record has been removed from the Store
9816          * @param {Store} this
9817          * @param {Roo.data.Record} record The Record that was removed
9818          * @param {Number} index The index at which the record was removed
9819          */
9820         remove : true,
9821         /**
9822          * @event update
9823          * Fires when a Record has been updated
9824          * @param {Store} this
9825          * @param {Roo.data.Record} record The Record that was updated
9826          * @param {String} operation The update operation being performed.  Value may be one of:
9827          * <pre><code>
9828  Roo.data.Record.EDIT
9829  Roo.data.Record.REJECT
9830  Roo.data.Record.COMMIT
9831          * </code></pre>
9832          */
9833         update : true,
9834         /**
9835          * @event clear
9836          * Fires when the data cache has been cleared.
9837          * @param {Store} this
9838          */
9839         clear : true,
9840         /**
9841          * @event beforeload
9842          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9843          * the load action will be canceled.
9844          * @param {Store} this
9845          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9846          */
9847         beforeload : true,
9848         /**
9849          * @event beforeloadadd
9850          * Fires after a new set of Records has been loaded.
9851          * @param {Store} this
9852          * @param {Roo.data.Record[]} records The Records that were loaded
9853          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9854          */
9855         beforeloadadd : true,
9856         /**
9857          * @event load
9858          * Fires after a new set of Records has been loaded, before they are added to the store.
9859          * @param {Store} this
9860          * @param {Roo.data.Record[]} records The Records that were loaded
9861          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9862          * @params {Object} return from reader
9863          */
9864         load : true,
9865         /**
9866          * @event loadexception
9867          * Fires if an exception occurs in the Proxy during loading.
9868          * Called with the signature of the Proxy's "loadexception" event.
9869          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9870          * 
9871          * @param {Proxy} 
9872          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9873          * @param {Object} load options 
9874          * @param {Object} jsonData from your request (normally this contains the Exception)
9875          */
9876         loadexception : true
9877     });
9878     
9879     if(this.proxy){
9880         this.proxy = Roo.factory(this.proxy, Roo.data);
9881         this.proxy.xmodule = this.xmodule || false;
9882         this.relayEvents(this.proxy,  ["loadexception"]);
9883     }
9884     this.sortToggle = {};
9885     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9886
9887     Roo.data.Store.superclass.constructor.call(this);
9888
9889     if(this.inlineData){
9890         this.loadData(this.inlineData);
9891         delete this.inlineData;
9892     }
9893 };
9894
9895 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9896      /**
9897     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9898     * without a remote query - used by combo/forms at present.
9899     */
9900     
9901     /**
9902     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9903     */
9904     /**
9905     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9906     */
9907     /**
9908     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9909     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9910     */
9911     /**
9912     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9913     * on any HTTP request
9914     */
9915     /**
9916     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9917     */
9918     /**
9919     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9920     */
9921     multiSort: false,
9922     /**
9923     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9924     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9925     */
9926     remoteSort : false,
9927
9928     /**
9929     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9930      * loaded or when a record is removed. (defaults to false).
9931     */
9932     pruneModifiedRecords : false,
9933
9934     // private
9935     lastOptions : null,
9936
9937     /**
9938      * Add Records to the Store and fires the add event.
9939      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9940      */
9941     add : function(records){
9942         records = [].concat(records);
9943         for(var i = 0, len = records.length; i < len; i++){
9944             records[i].join(this);
9945         }
9946         var index = this.data.length;
9947         this.data.addAll(records);
9948         this.fireEvent("add", this, records, index);
9949     },
9950
9951     /**
9952      * Remove a Record from the Store and fires the remove event.
9953      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9954      */
9955     remove : function(record){
9956         var index = this.data.indexOf(record);
9957         this.data.removeAt(index);
9958         if(this.pruneModifiedRecords){
9959             this.modified.remove(record);
9960         }
9961         this.fireEvent("remove", this, record, index);
9962     },
9963
9964     /**
9965      * Remove all Records from the Store and fires the clear event.
9966      */
9967     removeAll : function(){
9968         this.data.clear();
9969         if(this.pruneModifiedRecords){
9970             this.modified = [];
9971         }
9972         this.fireEvent("clear", this);
9973     },
9974
9975     /**
9976      * Inserts Records to the Store at the given index and fires the add event.
9977      * @param {Number} index The start index at which to insert the passed Records.
9978      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9979      */
9980     insert : function(index, records){
9981         records = [].concat(records);
9982         for(var i = 0, len = records.length; i < len; i++){
9983             this.data.insert(index, records[i]);
9984             records[i].join(this);
9985         }
9986         this.fireEvent("add", this, records, index);
9987     },
9988
9989     /**
9990      * Get the index within the cache of the passed Record.
9991      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9992      * @return {Number} The index of the passed Record. Returns -1 if not found.
9993      */
9994     indexOf : function(record){
9995         return this.data.indexOf(record);
9996     },
9997
9998     /**
9999      * Get the index within the cache of the Record with the passed id.
10000      * @param {String} id The id of the Record to find.
10001      * @return {Number} The index of the Record. Returns -1 if not found.
10002      */
10003     indexOfId : function(id){
10004         return this.data.indexOfKey(id);
10005     },
10006
10007     /**
10008      * Get the Record with the specified id.
10009      * @param {String} id The id of the Record to find.
10010      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10011      */
10012     getById : function(id){
10013         return this.data.key(id);
10014     },
10015
10016     /**
10017      * Get the Record at the specified index.
10018      * @param {Number} index The index of the Record to find.
10019      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10020      */
10021     getAt : function(index){
10022         return this.data.itemAt(index);
10023     },
10024
10025     /**
10026      * Returns a range of Records between specified indices.
10027      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10028      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10029      * @return {Roo.data.Record[]} An array of Records
10030      */
10031     getRange : function(start, end){
10032         return this.data.getRange(start, end);
10033     },
10034
10035     // private
10036     storeOptions : function(o){
10037         o = Roo.apply({}, o);
10038         delete o.callback;
10039         delete o.scope;
10040         this.lastOptions = o;
10041     },
10042
10043     /**
10044      * Loads the Record cache from the configured Proxy using the configured Reader.
10045      * <p>
10046      * If using remote paging, then the first load call must specify the <em>start</em>
10047      * and <em>limit</em> properties in the options.params property to establish the initial
10048      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10049      * <p>
10050      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10051      * and this call will return before the new data has been loaded. Perform any post-processing
10052      * in a callback function, or in a "load" event handler.</strong>
10053      * <p>
10054      * @param {Object} options An object containing properties which control loading options:<ul>
10055      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10056      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10057      * passed the following arguments:<ul>
10058      * <li>r : Roo.data.Record[]</li>
10059      * <li>options: Options object from the load call</li>
10060      * <li>success: Boolean success indicator</li></ul></li>
10061      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10062      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10063      * </ul>
10064      */
10065     load : function(options){
10066         options = options || {};
10067         if(this.fireEvent("beforeload", this, options) !== false){
10068             this.storeOptions(options);
10069             var p = Roo.apply(options.params || {}, this.baseParams);
10070             // if meta was not loaded from remote source.. try requesting it.
10071             if (!this.reader.metaFromRemote) {
10072                 p._requestMeta = 1;
10073             }
10074             if(this.sortInfo && this.remoteSort){
10075                 var pn = this.paramNames;
10076                 p[pn["sort"]] = this.sortInfo.field;
10077                 p[pn["dir"]] = this.sortInfo.direction;
10078             }
10079             if (this.multiSort) {
10080                 var pn = this.paramNames;
10081                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10082             }
10083             
10084             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10085         }
10086     },
10087
10088     /**
10089      * Reloads the Record cache from the configured Proxy using the configured Reader and
10090      * the options from the last load operation performed.
10091      * @param {Object} options (optional) An object containing properties which may override the options
10092      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10093      * the most recently used options are reused).
10094      */
10095     reload : function(options){
10096         this.load(Roo.applyIf(options||{}, this.lastOptions));
10097     },
10098
10099     // private
10100     // Called as a callback by the Reader during a load operation.
10101     loadRecords : function(o, options, success){
10102         if(!o || success === false){
10103             if(success !== false){
10104                 this.fireEvent("load", this, [], options, o);
10105             }
10106             if(options.callback){
10107                 options.callback.call(options.scope || this, [], options, false);
10108             }
10109             return;
10110         }
10111         // if data returned failure - throw an exception.
10112         if (o.success === false) {
10113             // show a message if no listener is registered.
10114             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10115                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10116             }
10117             // loadmask wil be hooked into this..
10118             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10119             return;
10120         }
10121         var r = o.records, t = o.totalRecords || r.length;
10122         
10123         this.fireEvent("beforeloadadd", this, r, options, o);
10124         
10125         if(!options || options.add !== true){
10126             if(this.pruneModifiedRecords){
10127                 this.modified = [];
10128             }
10129             for(var i = 0, len = r.length; i < len; i++){
10130                 r[i].join(this);
10131             }
10132             if(this.snapshot){
10133                 this.data = this.snapshot;
10134                 delete this.snapshot;
10135             }
10136             this.data.clear();
10137             this.data.addAll(r);
10138             this.totalLength = t;
10139             this.applySort();
10140             this.fireEvent("datachanged", this);
10141         }else{
10142             this.totalLength = Math.max(t, this.data.length+r.length);
10143             this.add(r);
10144         }
10145         this.fireEvent("load", this, r, options, o);
10146         if(options.callback){
10147             options.callback.call(options.scope || this, r, options, true);
10148         }
10149     },
10150
10151
10152     /**
10153      * Loads data from a passed data block. A Reader which understands the format of the data
10154      * must have been configured in the constructor.
10155      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10156      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10157      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10158      */
10159     loadData : function(o, append){
10160         var r = this.reader.readRecords(o);
10161         this.loadRecords(r, {add: append}, true);
10162     },
10163
10164     /**
10165      * Gets the number of cached records.
10166      * <p>
10167      * <em>If using paging, this may not be the total size of the dataset. If the data object
10168      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10169      * the data set size</em>
10170      */
10171     getCount : function(){
10172         return this.data.length || 0;
10173     },
10174
10175     /**
10176      * Gets the total number of records in the dataset as returned by the server.
10177      * <p>
10178      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10179      * the dataset size</em>
10180      */
10181     getTotalCount : function(){
10182         return this.totalLength || 0;
10183     },
10184
10185     /**
10186      * Returns the sort state of the Store as an object with two properties:
10187      * <pre><code>
10188  field {String} The name of the field by which the Records are sorted
10189  direction {String} The sort order, "ASC" or "DESC"
10190      * </code></pre>
10191      */
10192     getSortState : function(){
10193         return this.sortInfo;
10194     },
10195
10196     // private
10197     applySort : function(){
10198         if(this.sortInfo && !this.remoteSort){
10199             var s = this.sortInfo, f = s.field;
10200             var st = this.fields.get(f).sortType;
10201             var fn = function(r1, r2){
10202                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10203                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10204             };
10205             this.data.sort(s.direction, fn);
10206             if(this.snapshot && this.snapshot != this.data){
10207                 this.snapshot.sort(s.direction, fn);
10208             }
10209         }
10210     },
10211
10212     /**
10213      * Sets the default sort column and order to be used by the next load operation.
10214      * @param {String} fieldName The name of the field to sort by.
10215      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10216      */
10217     setDefaultSort : function(field, dir){
10218         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10219     },
10220
10221     /**
10222      * Sort the Records.
10223      * If remote sorting is used, the sort is performed on the server, and the cache is
10224      * reloaded. If local sorting is used, the cache is sorted internally.
10225      * @param {String} fieldName The name of the field to sort by.
10226      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10227      */
10228     sort : function(fieldName, dir){
10229         var f = this.fields.get(fieldName);
10230         if(!dir){
10231             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10232             
10233             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10234                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10235             }else{
10236                 dir = f.sortDir;
10237             }
10238         }
10239         this.sortToggle[f.name] = dir;
10240         this.sortInfo = {field: f.name, direction: dir};
10241         if(!this.remoteSort){
10242             this.applySort();
10243             this.fireEvent("datachanged", this);
10244         }else{
10245             this.load(this.lastOptions);
10246         }
10247     },
10248
10249     /**
10250      * Calls the specified function for each of the Records in the cache.
10251      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10252      * Returning <em>false</em> aborts and exits the iteration.
10253      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10254      */
10255     each : function(fn, scope){
10256         this.data.each(fn, scope);
10257     },
10258
10259     /**
10260      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10261      * (e.g., during paging).
10262      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10263      */
10264     getModifiedRecords : function(){
10265         return this.modified;
10266     },
10267
10268     // private
10269     createFilterFn : function(property, value, anyMatch){
10270         if(!value.exec){ // not a regex
10271             value = String(value);
10272             if(value.length == 0){
10273                 return false;
10274             }
10275             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10276         }
10277         return function(r){
10278             return value.test(r.data[property]);
10279         };
10280     },
10281
10282     /**
10283      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10284      * @param {String} property A field on your records
10285      * @param {Number} start The record index to start at (defaults to 0)
10286      * @param {Number} end The last record index to include (defaults to length - 1)
10287      * @return {Number} The sum
10288      */
10289     sum : function(property, start, end){
10290         var rs = this.data.items, v = 0;
10291         start = start || 0;
10292         end = (end || end === 0) ? end : rs.length-1;
10293
10294         for(var i = start; i <= end; i++){
10295             v += (rs[i].data[property] || 0);
10296         }
10297         return v;
10298     },
10299
10300     /**
10301      * Filter the records by a specified property.
10302      * @param {String} field A field on your records
10303      * @param {String/RegExp} value Either a string that the field
10304      * should start with or a RegExp to test against the field
10305      * @param {Boolean} anyMatch True to match any part not just the beginning
10306      */
10307     filter : function(property, value, anyMatch){
10308         var fn = this.createFilterFn(property, value, anyMatch);
10309         return fn ? this.filterBy(fn) : this.clearFilter();
10310     },
10311
10312     /**
10313      * Filter by a function. The specified function will be called with each
10314      * record in this data source. If the function returns true the record is included,
10315      * otherwise it is filtered.
10316      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10317      * @param {Object} scope (optional) The scope of the function (defaults to this)
10318      */
10319     filterBy : function(fn, scope){
10320         this.snapshot = this.snapshot || this.data;
10321         this.data = this.queryBy(fn, scope||this);
10322         this.fireEvent("datachanged", this);
10323     },
10324
10325     /**
10326      * Query the records by a specified property.
10327      * @param {String} field A field on your records
10328      * @param {String/RegExp} value Either a string that the field
10329      * should start with or a RegExp to test against the field
10330      * @param {Boolean} anyMatch True to match any part not just the beginning
10331      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10332      */
10333     query : function(property, value, anyMatch){
10334         var fn = this.createFilterFn(property, value, anyMatch);
10335         return fn ? this.queryBy(fn) : this.data.clone();
10336     },
10337
10338     /**
10339      * Query by a function. The specified function will be called with each
10340      * record in this data source. If the function returns true the record is included
10341      * in the results.
10342      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10343      * @param {Object} scope (optional) The scope of the function (defaults to this)
10344       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10345      **/
10346     queryBy : function(fn, scope){
10347         var data = this.snapshot || this.data;
10348         return data.filterBy(fn, scope||this);
10349     },
10350
10351     /**
10352      * Collects unique values for a particular dataIndex from this store.
10353      * @param {String} dataIndex The property to collect
10354      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10355      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10356      * @return {Array} An array of the unique values
10357      **/
10358     collect : function(dataIndex, allowNull, bypassFilter){
10359         var d = (bypassFilter === true && this.snapshot) ?
10360                 this.snapshot.items : this.data.items;
10361         var v, sv, r = [], l = {};
10362         for(var i = 0, len = d.length; i < len; i++){
10363             v = d[i].data[dataIndex];
10364             sv = String(v);
10365             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10366                 l[sv] = true;
10367                 r[r.length] = v;
10368             }
10369         }
10370         return r;
10371     },
10372
10373     /**
10374      * Revert to a view of the Record cache with no filtering applied.
10375      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10376      */
10377     clearFilter : function(suppressEvent){
10378         if(this.snapshot && this.snapshot != this.data){
10379             this.data = this.snapshot;
10380             delete this.snapshot;
10381             if(suppressEvent !== true){
10382                 this.fireEvent("datachanged", this);
10383             }
10384         }
10385     },
10386
10387     // private
10388     afterEdit : function(record){
10389         if(this.modified.indexOf(record) == -1){
10390             this.modified.push(record);
10391         }
10392         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10393     },
10394     
10395     // private
10396     afterReject : function(record){
10397         this.modified.remove(record);
10398         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10399     },
10400
10401     // private
10402     afterCommit : function(record){
10403         this.modified.remove(record);
10404         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10405     },
10406
10407     /**
10408      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10409      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10410      */
10411     commitChanges : function(){
10412         var m = this.modified.slice(0);
10413         this.modified = [];
10414         for(var i = 0, len = m.length; i < len; i++){
10415             m[i].commit();
10416         }
10417     },
10418
10419     /**
10420      * Cancel outstanding changes on all changed records.
10421      */
10422     rejectChanges : function(){
10423         var m = this.modified.slice(0);
10424         this.modified = [];
10425         for(var i = 0, len = m.length; i < len; i++){
10426             m[i].reject();
10427         }
10428     },
10429
10430     onMetaChange : function(meta, rtype, o){
10431         this.recordType = rtype;
10432         this.fields = rtype.prototype.fields;
10433         delete this.snapshot;
10434         this.sortInfo = meta.sortInfo || this.sortInfo;
10435         this.modified = [];
10436         this.fireEvent('metachange', this, this.reader.meta);
10437     },
10438     
10439     moveIndex : function(data, type)
10440     {
10441         var index = this.indexOf(data);
10442         
10443         var newIndex = index + type;
10444         
10445         this.remove(data);
10446         
10447         this.insert(newIndex, data);
10448         
10449     }
10450 });/*
10451  * Based on:
10452  * Ext JS Library 1.1.1
10453  * Copyright(c) 2006-2007, Ext JS, LLC.
10454  *
10455  * Originally Released Under LGPL - original licence link has changed is not relivant.
10456  *
10457  * Fork - LGPL
10458  * <script type="text/javascript">
10459  */
10460
10461 /**
10462  * @class Roo.data.SimpleStore
10463  * @extends Roo.data.Store
10464  * Small helper class to make creating Stores from Array data easier.
10465  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10466  * @cfg {Array} fields An array of field definition objects, or field name strings.
10467  * @cfg {Array} data The multi-dimensional array of data
10468  * @constructor
10469  * @param {Object} config
10470  */
10471 Roo.data.SimpleStore = function(config){
10472     Roo.data.SimpleStore.superclass.constructor.call(this, {
10473         isLocal : true,
10474         reader: new Roo.data.ArrayReader({
10475                 id: config.id
10476             },
10477             Roo.data.Record.create(config.fields)
10478         ),
10479         proxy : new Roo.data.MemoryProxy(config.data)
10480     });
10481     this.load();
10482 };
10483 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10484  * Based on:
10485  * Ext JS Library 1.1.1
10486  * Copyright(c) 2006-2007, Ext JS, LLC.
10487  *
10488  * Originally Released Under LGPL - original licence link has changed is not relivant.
10489  *
10490  * Fork - LGPL
10491  * <script type="text/javascript">
10492  */
10493
10494 /**
10495 /**
10496  * @extends Roo.data.Store
10497  * @class Roo.data.JsonStore
10498  * Small helper class to make creating Stores for JSON data easier. <br/>
10499 <pre><code>
10500 var store = new Roo.data.JsonStore({
10501     url: 'get-images.php',
10502     root: 'images',
10503     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10504 });
10505 </code></pre>
10506  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10507  * JsonReader and HttpProxy (unless inline data is provided).</b>
10508  * @cfg {Array} fields An array of field definition objects, or field name strings.
10509  * @constructor
10510  * @param {Object} config
10511  */
10512 Roo.data.JsonStore = function(c){
10513     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10514         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10515         reader: new Roo.data.JsonReader(c, c.fields)
10516     }));
10517 };
10518 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10519  * Based on:
10520  * Ext JS Library 1.1.1
10521  * Copyright(c) 2006-2007, Ext JS, LLC.
10522  *
10523  * Originally Released Under LGPL - original licence link has changed is not relivant.
10524  *
10525  * Fork - LGPL
10526  * <script type="text/javascript">
10527  */
10528
10529  
10530 Roo.data.Field = function(config){
10531     if(typeof config == "string"){
10532         config = {name: config};
10533     }
10534     Roo.apply(this, config);
10535     
10536     if(!this.type){
10537         this.type = "auto";
10538     }
10539     
10540     var st = Roo.data.SortTypes;
10541     // named sortTypes are supported, here we look them up
10542     if(typeof this.sortType == "string"){
10543         this.sortType = st[this.sortType];
10544     }
10545     
10546     // set default sortType for strings and dates
10547     if(!this.sortType){
10548         switch(this.type){
10549             case "string":
10550                 this.sortType = st.asUCString;
10551                 break;
10552             case "date":
10553                 this.sortType = st.asDate;
10554                 break;
10555             default:
10556                 this.sortType = st.none;
10557         }
10558     }
10559
10560     // define once
10561     var stripRe = /[\$,%]/g;
10562
10563     // prebuilt conversion function for this field, instead of
10564     // switching every time we're reading a value
10565     if(!this.convert){
10566         var cv, dateFormat = this.dateFormat;
10567         switch(this.type){
10568             case "":
10569             case "auto":
10570             case undefined:
10571                 cv = function(v){ return v; };
10572                 break;
10573             case "string":
10574                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10575                 break;
10576             case "int":
10577                 cv = function(v){
10578                     return v !== undefined && v !== null && v !== '' ?
10579                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10580                     };
10581                 break;
10582             case "float":
10583                 cv = function(v){
10584                     return v !== undefined && v !== null && v !== '' ?
10585                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10586                     };
10587                 break;
10588             case "bool":
10589             case "boolean":
10590                 cv = function(v){ return v === true || v === "true" || v == 1; };
10591                 break;
10592             case "date":
10593                 cv = function(v){
10594                     if(!v){
10595                         return '';
10596                     }
10597                     if(v instanceof Date){
10598                         return v;
10599                     }
10600                     if(dateFormat){
10601                         if(dateFormat == "timestamp"){
10602                             return new Date(v*1000);
10603                         }
10604                         return Date.parseDate(v, dateFormat);
10605                     }
10606                     var parsed = Date.parse(v);
10607                     return parsed ? new Date(parsed) : null;
10608                 };
10609              break;
10610             
10611         }
10612         this.convert = cv;
10613     }
10614 };
10615
10616 Roo.data.Field.prototype = {
10617     dateFormat: null,
10618     defaultValue: "",
10619     mapping: null,
10620     sortType : null,
10621     sortDir : "ASC"
10622 };/*
10623  * Based on:
10624  * Ext JS Library 1.1.1
10625  * Copyright(c) 2006-2007, Ext JS, LLC.
10626  *
10627  * Originally Released Under LGPL - original licence link has changed is not relivant.
10628  *
10629  * Fork - LGPL
10630  * <script type="text/javascript">
10631  */
10632  
10633 // Base class for reading structured data from a data source.  This class is intended to be
10634 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10635
10636 /**
10637  * @class Roo.data.DataReader
10638  * Base class for reading structured data from a data source.  This class is intended to be
10639  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10640  */
10641
10642 Roo.data.DataReader = function(meta, recordType){
10643     
10644     this.meta = meta;
10645     
10646     this.recordType = recordType instanceof Array ? 
10647         Roo.data.Record.create(recordType) : recordType;
10648 };
10649
10650 Roo.data.DataReader.prototype = {
10651      /**
10652      * Create an empty record
10653      * @param {Object} data (optional) - overlay some values
10654      * @return {Roo.data.Record} record created.
10655      */
10656     newRow :  function(d) {
10657         var da =  {};
10658         this.recordType.prototype.fields.each(function(c) {
10659             switch( c.type) {
10660                 case 'int' : da[c.name] = 0; break;
10661                 case 'date' : da[c.name] = new Date(); break;
10662                 case 'float' : da[c.name] = 0.0; break;
10663                 case 'boolean' : da[c.name] = false; break;
10664                 default : da[c.name] = ""; break;
10665             }
10666             
10667         });
10668         return new this.recordType(Roo.apply(da, d));
10669     }
10670     
10671 };/*
10672  * Based on:
10673  * Ext JS Library 1.1.1
10674  * Copyright(c) 2006-2007, Ext JS, LLC.
10675  *
10676  * Originally Released Under LGPL - original licence link has changed is not relivant.
10677  *
10678  * Fork - LGPL
10679  * <script type="text/javascript">
10680  */
10681
10682 /**
10683  * @class Roo.data.DataProxy
10684  * @extends Roo.data.Observable
10685  * This class is an abstract base class for implementations which provide retrieval of
10686  * unformatted data objects.<br>
10687  * <p>
10688  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10689  * (of the appropriate type which knows how to parse the data object) to provide a block of
10690  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10691  * <p>
10692  * Custom implementations must implement the load method as described in
10693  * {@link Roo.data.HttpProxy#load}.
10694  */
10695 Roo.data.DataProxy = function(){
10696     this.addEvents({
10697         /**
10698          * @event beforeload
10699          * Fires before a network request is made to retrieve a data object.
10700          * @param {Object} This DataProxy object.
10701          * @param {Object} params The params parameter to the load function.
10702          */
10703         beforeload : true,
10704         /**
10705          * @event load
10706          * Fires before the load method's callback is called.
10707          * @param {Object} This DataProxy object.
10708          * @param {Object} o The data object.
10709          * @param {Object} arg The callback argument object passed to the load function.
10710          */
10711         load : true,
10712         /**
10713          * @event loadexception
10714          * Fires if an Exception occurs during data retrieval.
10715          * @param {Object} This DataProxy object.
10716          * @param {Object} o The data object.
10717          * @param {Object} arg The callback argument object passed to the load function.
10718          * @param {Object} e The Exception.
10719          */
10720         loadexception : true
10721     });
10722     Roo.data.DataProxy.superclass.constructor.call(this);
10723 };
10724
10725 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10726
10727     /**
10728      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10729      */
10730 /*
10731  * Based on:
10732  * Ext JS Library 1.1.1
10733  * Copyright(c) 2006-2007, Ext JS, LLC.
10734  *
10735  * Originally Released Under LGPL - original licence link has changed is not relivant.
10736  *
10737  * Fork - LGPL
10738  * <script type="text/javascript">
10739  */
10740 /**
10741  * @class Roo.data.MemoryProxy
10742  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10743  * to the Reader when its load method is called.
10744  * @constructor
10745  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10746  */
10747 Roo.data.MemoryProxy = function(data){
10748     if (data.data) {
10749         data = data.data;
10750     }
10751     Roo.data.MemoryProxy.superclass.constructor.call(this);
10752     this.data = data;
10753 };
10754
10755 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10756     /**
10757      * Load data from the requested source (in this case an in-memory
10758      * data object passed to the constructor), read the data object into
10759      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10760      * process that block using the passed callback.
10761      * @param {Object} params This parameter is not used by the MemoryProxy class.
10762      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10763      * object into a block of Roo.data.Records.
10764      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10765      * The function must be passed <ul>
10766      * <li>The Record block object</li>
10767      * <li>The "arg" argument from the load function</li>
10768      * <li>A boolean success indicator</li>
10769      * </ul>
10770      * @param {Object} scope The scope in which to call the callback
10771      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10772      */
10773     load : function(params, reader, callback, scope, arg){
10774         params = params || {};
10775         var result;
10776         try {
10777             result = reader.readRecords(this.data);
10778         }catch(e){
10779             this.fireEvent("loadexception", this, arg, null, e);
10780             callback.call(scope, null, arg, false);
10781             return;
10782         }
10783         callback.call(scope, result, arg, true);
10784     },
10785     
10786     // private
10787     update : function(params, records){
10788         
10789     }
10790 });/*
10791  * Based on:
10792  * Ext JS Library 1.1.1
10793  * Copyright(c) 2006-2007, Ext JS, LLC.
10794  *
10795  * Originally Released Under LGPL - original licence link has changed is not relivant.
10796  *
10797  * Fork - LGPL
10798  * <script type="text/javascript">
10799  */
10800 /**
10801  * @class Roo.data.HttpProxy
10802  * @extends Roo.data.DataProxy
10803  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10804  * configured to reference a certain URL.<br><br>
10805  * <p>
10806  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10807  * from which the running page was served.<br><br>
10808  * <p>
10809  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10810  * <p>
10811  * Be aware that to enable the browser to parse an XML document, the server must set
10812  * the Content-Type header in the HTTP response to "text/xml".
10813  * @constructor
10814  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10815  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10816  * will be used to make the request.
10817  */
10818 Roo.data.HttpProxy = function(conn){
10819     Roo.data.HttpProxy.superclass.constructor.call(this);
10820     // is conn a conn config or a real conn?
10821     this.conn = conn;
10822     this.useAjax = !conn || !conn.events;
10823   
10824 };
10825
10826 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10827     // thse are take from connection...
10828     
10829     /**
10830      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10831      */
10832     /**
10833      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10834      * extra parameters to each request made by this object. (defaults to undefined)
10835      */
10836     /**
10837      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10838      *  to each request made by this object. (defaults to undefined)
10839      */
10840     /**
10841      * @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)
10842      */
10843     /**
10844      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10845      */
10846      /**
10847      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10848      * @type Boolean
10849      */
10850   
10851
10852     /**
10853      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10854      * @type Boolean
10855      */
10856     /**
10857      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10858      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10859      * a finer-grained basis than the DataProxy events.
10860      */
10861     getConnection : function(){
10862         return this.useAjax ? Roo.Ajax : this.conn;
10863     },
10864
10865     /**
10866      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10867      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10868      * process that block using the passed callback.
10869      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10870      * for the request to the remote server.
10871      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10872      * object into a block of Roo.data.Records.
10873      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10874      * The function must be passed <ul>
10875      * <li>The Record block object</li>
10876      * <li>The "arg" argument from the load function</li>
10877      * <li>A boolean success indicator</li>
10878      * </ul>
10879      * @param {Object} scope The scope in which to call the callback
10880      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10881      */
10882     load : function(params, reader, callback, scope, arg){
10883         if(this.fireEvent("beforeload", this, params) !== false){
10884             var  o = {
10885                 params : params || {},
10886                 request: {
10887                     callback : callback,
10888                     scope : scope,
10889                     arg : arg
10890                 },
10891                 reader: reader,
10892                 callback : this.loadResponse,
10893                 scope: this
10894             };
10895             if(this.useAjax){
10896                 Roo.applyIf(o, this.conn);
10897                 if(this.activeRequest){
10898                     Roo.Ajax.abort(this.activeRequest);
10899                 }
10900                 this.activeRequest = Roo.Ajax.request(o);
10901             }else{
10902                 this.conn.request(o);
10903             }
10904         }else{
10905             callback.call(scope||this, null, arg, false);
10906         }
10907     },
10908
10909     // private
10910     loadResponse : function(o, success, response){
10911         delete this.activeRequest;
10912         if(!success){
10913             this.fireEvent("loadexception", this, o, response);
10914             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10915             return;
10916         }
10917         var result;
10918         try {
10919             result = o.reader.read(response);
10920         }catch(e){
10921             this.fireEvent("loadexception", this, o, response, e);
10922             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10923             return;
10924         }
10925         
10926         this.fireEvent("load", this, o, o.request.arg);
10927         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10928     },
10929
10930     // private
10931     update : function(dataSet){
10932
10933     },
10934
10935     // private
10936     updateResponse : function(dataSet){
10937
10938     }
10939 });/*
10940  * Based on:
10941  * Ext JS Library 1.1.1
10942  * Copyright(c) 2006-2007, Ext JS, LLC.
10943  *
10944  * Originally Released Under LGPL - original licence link has changed is not relivant.
10945  *
10946  * Fork - LGPL
10947  * <script type="text/javascript">
10948  */
10949
10950 /**
10951  * @class Roo.data.ScriptTagProxy
10952  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10953  * other than the originating domain of the running page.<br><br>
10954  * <p>
10955  * <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
10956  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10957  * <p>
10958  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10959  * source code that is used as the source inside a &lt;script> tag.<br><br>
10960  * <p>
10961  * In order for the browser to process the returned data, the server must wrap the data object
10962  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10963  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10964  * depending on whether the callback name was passed:
10965  * <p>
10966  * <pre><code>
10967 boolean scriptTag = false;
10968 String cb = request.getParameter("callback");
10969 if (cb != null) {
10970     scriptTag = true;
10971     response.setContentType("text/javascript");
10972 } else {
10973     response.setContentType("application/x-json");
10974 }
10975 Writer out = response.getWriter();
10976 if (scriptTag) {
10977     out.write(cb + "(");
10978 }
10979 out.print(dataBlock.toJsonString());
10980 if (scriptTag) {
10981     out.write(");");
10982 }
10983 </pre></code>
10984  *
10985  * @constructor
10986  * @param {Object} config A configuration object.
10987  */
10988 Roo.data.ScriptTagProxy = function(config){
10989     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10990     Roo.apply(this, config);
10991     this.head = document.getElementsByTagName("head")[0];
10992 };
10993
10994 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10995
10996 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10997     /**
10998      * @cfg {String} url The URL from which to request the data object.
10999      */
11000     /**
11001      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11002      */
11003     timeout : 30000,
11004     /**
11005      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11006      * the server the name of the callback function set up by the load call to process the returned data object.
11007      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11008      * javascript output which calls this named function passing the data object as its only parameter.
11009      */
11010     callbackParam : "callback",
11011     /**
11012      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11013      * name to the request.
11014      */
11015     nocache : true,
11016
11017     /**
11018      * Load data from the configured URL, read the data object into
11019      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11020      * process that block using the passed callback.
11021      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11022      * for the request to the remote server.
11023      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11024      * object into a block of Roo.data.Records.
11025      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11026      * The function must be passed <ul>
11027      * <li>The Record block object</li>
11028      * <li>The "arg" argument from the load function</li>
11029      * <li>A boolean success indicator</li>
11030      * </ul>
11031      * @param {Object} scope The scope in which to call the callback
11032      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11033      */
11034     load : function(params, reader, callback, scope, arg){
11035         if(this.fireEvent("beforeload", this, params) !== false){
11036
11037             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11038
11039             var url = this.url;
11040             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11041             if(this.nocache){
11042                 url += "&_dc=" + (new Date().getTime());
11043             }
11044             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11045             var trans = {
11046                 id : transId,
11047                 cb : "stcCallback"+transId,
11048                 scriptId : "stcScript"+transId,
11049                 params : params,
11050                 arg : arg,
11051                 url : url,
11052                 callback : callback,
11053                 scope : scope,
11054                 reader : reader
11055             };
11056             var conn = this;
11057
11058             window[trans.cb] = function(o){
11059                 conn.handleResponse(o, trans);
11060             };
11061
11062             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11063
11064             if(this.autoAbort !== false){
11065                 this.abort();
11066             }
11067
11068             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11069
11070             var script = document.createElement("script");
11071             script.setAttribute("src", url);
11072             script.setAttribute("type", "text/javascript");
11073             script.setAttribute("id", trans.scriptId);
11074             this.head.appendChild(script);
11075
11076             this.trans = trans;
11077         }else{
11078             callback.call(scope||this, null, arg, false);
11079         }
11080     },
11081
11082     // private
11083     isLoading : function(){
11084         return this.trans ? true : false;
11085     },
11086
11087     /**
11088      * Abort the current server request.
11089      */
11090     abort : function(){
11091         if(this.isLoading()){
11092             this.destroyTrans(this.trans);
11093         }
11094     },
11095
11096     // private
11097     destroyTrans : function(trans, isLoaded){
11098         this.head.removeChild(document.getElementById(trans.scriptId));
11099         clearTimeout(trans.timeoutId);
11100         if(isLoaded){
11101             window[trans.cb] = undefined;
11102             try{
11103                 delete window[trans.cb];
11104             }catch(e){}
11105         }else{
11106             // if hasn't been loaded, wait for load to remove it to prevent script error
11107             window[trans.cb] = function(){
11108                 window[trans.cb] = undefined;
11109                 try{
11110                     delete window[trans.cb];
11111                 }catch(e){}
11112             };
11113         }
11114     },
11115
11116     // private
11117     handleResponse : function(o, trans){
11118         this.trans = false;
11119         this.destroyTrans(trans, true);
11120         var result;
11121         try {
11122             result = trans.reader.readRecords(o);
11123         }catch(e){
11124             this.fireEvent("loadexception", this, o, trans.arg, e);
11125             trans.callback.call(trans.scope||window, null, trans.arg, false);
11126             return;
11127         }
11128         this.fireEvent("load", this, o, trans.arg);
11129         trans.callback.call(trans.scope||window, result, trans.arg, true);
11130     },
11131
11132     // private
11133     handleFailure : function(trans){
11134         this.trans = false;
11135         this.destroyTrans(trans, false);
11136         this.fireEvent("loadexception", this, null, trans.arg);
11137         trans.callback.call(trans.scope||window, null, trans.arg, false);
11138     }
11139 });/*
11140  * Based on:
11141  * Ext JS Library 1.1.1
11142  * Copyright(c) 2006-2007, Ext JS, LLC.
11143  *
11144  * Originally Released Under LGPL - original licence link has changed is not relivant.
11145  *
11146  * Fork - LGPL
11147  * <script type="text/javascript">
11148  */
11149
11150 /**
11151  * @class Roo.data.JsonReader
11152  * @extends Roo.data.DataReader
11153  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11154  * based on mappings in a provided Roo.data.Record constructor.
11155  * 
11156  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11157  * in the reply previously. 
11158  * 
11159  * <p>
11160  * Example code:
11161  * <pre><code>
11162 var RecordDef = Roo.data.Record.create([
11163     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11164     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11165 ]);
11166 var myReader = new Roo.data.JsonReader({
11167     totalProperty: "results",    // The property which contains the total dataset size (optional)
11168     root: "rows",                // The property which contains an Array of row objects
11169     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11170 }, RecordDef);
11171 </code></pre>
11172  * <p>
11173  * This would consume a JSON file like this:
11174  * <pre><code>
11175 { 'results': 2, 'rows': [
11176     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11177     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11178 }
11179 </code></pre>
11180  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11181  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11182  * paged from the remote server.
11183  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11184  * @cfg {String} root name of the property which contains the Array of row objects.
11185  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11186  * @cfg {Array} fields Array of field definition objects
11187  * @constructor
11188  * Create a new JsonReader
11189  * @param {Object} meta Metadata configuration options
11190  * @param {Object} recordType Either an Array of field definition objects,
11191  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11192  */
11193 Roo.data.JsonReader = function(meta, recordType){
11194     
11195     meta = meta || {};
11196     // set some defaults:
11197     Roo.applyIf(meta, {
11198         totalProperty: 'total',
11199         successProperty : 'success',
11200         root : 'data',
11201         id : 'id'
11202     });
11203     
11204     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11205 };
11206 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11207     
11208     /**
11209      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11210      * Used by Store query builder to append _requestMeta to params.
11211      * 
11212      */
11213     metaFromRemote : false,
11214     /**
11215      * This method is only used by a DataProxy which has retrieved data from a remote server.
11216      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11217      * @return {Object} data A data block which is used by an Roo.data.Store object as
11218      * a cache of Roo.data.Records.
11219      */
11220     read : function(response){
11221         var json = response.responseText;
11222        
11223         var o = /* eval:var:o */ eval("("+json+")");
11224         if(!o) {
11225             throw {message: "JsonReader.read: Json object not found"};
11226         }
11227         
11228         if(o.metaData){
11229             
11230             delete this.ef;
11231             this.metaFromRemote = true;
11232             this.meta = o.metaData;
11233             this.recordType = Roo.data.Record.create(o.metaData.fields);
11234             this.onMetaChange(this.meta, this.recordType, o);
11235         }
11236         return this.readRecords(o);
11237     },
11238
11239     // private function a store will implement
11240     onMetaChange : function(meta, recordType, o){
11241
11242     },
11243
11244     /**
11245          * @ignore
11246          */
11247     simpleAccess: function(obj, subsc) {
11248         return obj[subsc];
11249     },
11250
11251         /**
11252          * @ignore
11253          */
11254     getJsonAccessor: function(){
11255         var re = /[\[\.]/;
11256         return function(expr) {
11257             try {
11258                 return(re.test(expr))
11259                     ? new Function("obj", "return obj." + expr)
11260                     : function(obj){
11261                         return obj[expr];
11262                     };
11263             } catch(e){}
11264             return Roo.emptyFn;
11265         };
11266     }(),
11267
11268     /**
11269      * Create a data block containing Roo.data.Records from an XML document.
11270      * @param {Object} o An object which contains an Array of row objects in the property specified
11271      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11272      * which contains the total size of the dataset.
11273      * @return {Object} data A data block which is used by an Roo.data.Store object as
11274      * a cache of Roo.data.Records.
11275      */
11276     readRecords : function(o){
11277         /**
11278          * After any data loads, the raw JSON data is available for further custom processing.
11279          * @type Object
11280          */
11281         this.o = o;
11282         var s = this.meta, Record = this.recordType,
11283             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11284
11285 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11286         if (!this.ef) {
11287             if(s.totalProperty) {
11288                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11289                 }
11290                 if(s.successProperty) {
11291                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11292                 }
11293                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11294                 if (s.id) {
11295                         var g = this.getJsonAccessor(s.id);
11296                         this.getId = function(rec) {
11297                                 var r = g(rec);  
11298                                 return (r === undefined || r === "") ? null : r;
11299                         };
11300                 } else {
11301                         this.getId = function(){return null;};
11302                 }
11303             this.ef = [];
11304             for(var jj = 0; jj < fl; jj++){
11305                 f = fi[jj];
11306                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11307                 this.ef[jj] = this.getJsonAccessor(map);
11308             }
11309         }
11310
11311         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11312         if(s.totalProperty){
11313             var vt = parseInt(this.getTotal(o), 10);
11314             if(!isNaN(vt)){
11315                 totalRecords = vt;
11316             }
11317         }
11318         if(s.successProperty){
11319             var vs = this.getSuccess(o);
11320             if(vs === false || vs === 'false'){
11321                 success = false;
11322             }
11323         }
11324         var records = [];
11325         for(var i = 0; i < c; i++){
11326                 var n = root[i];
11327             var values = {};
11328             var id = this.getId(n);
11329             for(var j = 0; j < fl; j++){
11330                 f = fi[j];
11331             var v = this.ef[j](n);
11332             if (!f.convert) {
11333                 Roo.log('missing convert for ' + f.name);
11334                 Roo.log(f);
11335                 continue;
11336             }
11337             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11338             }
11339             var record = new Record(values, id);
11340             record.json = n;
11341             records[i] = record;
11342         }
11343         return {
11344             raw : o,
11345             success : success,
11346             records : records,
11347             totalRecords : totalRecords
11348         };
11349     }
11350 });/*
11351  * Based on:
11352  * Ext JS Library 1.1.1
11353  * Copyright(c) 2006-2007, Ext JS, LLC.
11354  *
11355  * Originally Released Under LGPL - original licence link has changed is not relivant.
11356  *
11357  * Fork - LGPL
11358  * <script type="text/javascript">
11359  */
11360
11361 /**
11362  * @class Roo.data.ArrayReader
11363  * @extends Roo.data.DataReader
11364  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11365  * Each element of that Array represents a row of data fields. The
11366  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11367  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11368  * <p>
11369  * Example code:.
11370  * <pre><code>
11371 var RecordDef = Roo.data.Record.create([
11372     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11373     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11374 ]);
11375 var myReader = new Roo.data.ArrayReader({
11376     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11377 }, RecordDef);
11378 </code></pre>
11379  * <p>
11380  * This would consume an Array like this:
11381  * <pre><code>
11382 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11383   </code></pre>
11384  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11385  * @constructor
11386  * Create a new JsonReader
11387  * @param {Object} meta Metadata configuration options.
11388  * @param {Object} recordType Either an Array of field definition objects
11389  * as specified to {@link Roo.data.Record#create},
11390  * or an {@link Roo.data.Record} object
11391  * created using {@link Roo.data.Record#create}.
11392  */
11393 Roo.data.ArrayReader = function(meta, recordType){
11394     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11395 };
11396
11397 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11398     /**
11399      * Create a data block containing Roo.data.Records from an XML document.
11400      * @param {Object} o An Array of row objects which represents the dataset.
11401      * @return {Object} data A data block which is used by an Roo.data.Store object as
11402      * a cache of Roo.data.Records.
11403      */
11404     readRecords : function(o){
11405         var sid = this.meta ? this.meta.id : null;
11406         var recordType = this.recordType, fields = recordType.prototype.fields;
11407         var records = [];
11408         var root = o;
11409             for(var i = 0; i < root.length; i++){
11410                     var n = root[i];
11411                 var values = {};
11412                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11413                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11414                 var f = fields.items[j];
11415                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11416                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11417                 v = f.convert(v);
11418                 values[f.name] = v;
11419             }
11420                 var record = new recordType(values, id);
11421                 record.json = n;
11422                 records[records.length] = record;
11423             }
11424             return {
11425                 records : records,
11426                 totalRecords : records.length
11427             };
11428     }
11429 });/*
11430  * - LGPL
11431  * * 
11432  */
11433
11434 /**
11435  * @class Roo.bootstrap.ComboBox
11436  * @extends Roo.bootstrap.TriggerField
11437  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11438  * @cfg {Boolean} append (true|false) default false
11439  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11440  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11441  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11442  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11443  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11444  * @cfg {Boolean} animate default true
11445  * @cfg {Boolean} emptyResultText only for touch device
11446  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11447  * @constructor
11448  * Create a new ComboBox.
11449  * @param {Object} config Configuration options
11450  */
11451 Roo.bootstrap.ComboBox = function(config){
11452     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11453     this.addEvents({
11454         /**
11455          * @event expand
11456          * Fires when the dropdown list is expanded
11457              * @param {Roo.bootstrap.ComboBox} combo This combo box
11458              */
11459         'expand' : true,
11460         /**
11461          * @event collapse
11462          * Fires when the dropdown list is collapsed
11463              * @param {Roo.bootstrap.ComboBox} combo This combo box
11464              */
11465         'collapse' : true,
11466         /**
11467          * @event beforeselect
11468          * Fires before a list item is selected. Return false to cancel the selection.
11469              * @param {Roo.bootstrap.ComboBox} combo This combo box
11470              * @param {Roo.data.Record} record The data record returned from the underlying store
11471              * @param {Number} index The index of the selected item in the dropdown list
11472              */
11473         'beforeselect' : true,
11474         /**
11475          * @event select
11476          * Fires when a list item is selected
11477              * @param {Roo.bootstrap.ComboBox} combo This combo box
11478              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11479              * @param {Number} index The index of the selected item in the dropdown list
11480              */
11481         'select' : true,
11482         /**
11483          * @event beforequery
11484          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11485          * The event object passed has these properties:
11486              * @param {Roo.bootstrap.ComboBox} combo This combo box
11487              * @param {String} query The query
11488              * @param {Boolean} forceAll true to force "all" query
11489              * @param {Boolean} cancel true to cancel the query
11490              * @param {Object} e The query event object
11491              */
11492         'beforequery': true,
11493          /**
11494          * @event add
11495          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11496              * @param {Roo.bootstrap.ComboBox} combo This combo box
11497              */
11498         'add' : true,
11499         /**
11500          * @event edit
11501          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11502              * @param {Roo.bootstrap.ComboBox} combo This combo box
11503              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11504              */
11505         'edit' : true,
11506         /**
11507          * @event remove
11508          * Fires when the remove value from the combobox array
11509              * @param {Roo.bootstrap.ComboBox} combo This combo box
11510              */
11511         'remove' : true,
11512         /**
11513          * @event specialfilter
11514          * Fires when specialfilter
11515             * @param {Roo.bootstrap.ComboBox} combo This combo box
11516             */
11517         'specialfilter' : true,
11518         /**
11519          * @event tick
11520          * Fires when tick the element
11521             * @param {Roo.bootstrap.ComboBox} combo This combo box
11522             */
11523         'tick' : true,
11524         /**
11525          * @event touchviewdisplay
11526          * Fires when touch view require special display (default is using displayField)
11527             * @param {Roo.bootstrap.ComboBox} combo This combo box
11528             * @param {Object} cfg set html .
11529             */
11530         'touchviewdisplay' : true
11531         
11532     });
11533     
11534     this.item = [];
11535     this.tickItems = [];
11536     
11537     this.selectedIndex = -1;
11538     if(this.mode == 'local'){
11539         if(config.queryDelay === undefined){
11540             this.queryDelay = 10;
11541         }
11542         if(config.minChars === undefined){
11543             this.minChars = 0;
11544         }
11545     }
11546 };
11547
11548 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11549      
11550     /**
11551      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11552      * rendering into an Roo.Editor, defaults to false)
11553      */
11554     /**
11555      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11556      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11557      */
11558     /**
11559      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11560      */
11561     /**
11562      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11563      * the dropdown list (defaults to undefined, with no header element)
11564      */
11565
11566      /**
11567      * @cfg {String/Roo.Template} tpl The template to use to render the output
11568      */
11569      
11570      /**
11571      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11572      */
11573     listWidth: undefined,
11574     /**
11575      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11576      * mode = 'remote' or 'text' if mode = 'local')
11577      */
11578     displayField: undefined,
11579     
11580     /**
11581      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11582      * mode = 'remote' or 'value' if mode = 'local'). 
11583      * Note: use of a valueField requires the user make a selection
11584      * in order for a value to be mapped.
11585      */
11586     valueField: undefined,
11587     
11588     
11589     /**
11590      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11591      * field's data value (defaults to the underlying DOM element's name)
11592      */
11593     hiddenName: undefined,
11594     /**
11595      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11596      */
11597     listClass: '',
11598     /**
11599      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11600      */
11601     selectedClass: 'active',
11602     
11603     /**
11604      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11605      */
11606     shadow:'sides',
11607     /**
11608      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11609      * anchor positions (defaults to 'tl-bl')
11610      */
11611     listAlign: 'tl-bl?',
11612     /**
11613      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11614      */
11615     maxHeight: 300,
11616     /**
11617      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11618      * query specified by the allQuery config option (defaults to 'query')
11619      */
11620     triggerAction: 'query',
11621     /**
11622      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11623      * (defaults to 4, does not apply if editable = false)
11624      */
11625     minChars : 4,
11626     /**
11627      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11628      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11629      */
11630     typeAhead: false,
11631     /**
11632      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11633      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11634      */
11635     queryDelay: 500,
11636     /**
11637      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11638      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11639      */
11640     pageSize: 0,
11641     /**
11642      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11643      * when editable = true (defaults to false)
11644      */
11645     selectOnFocus:false,
11646     /**
11647      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11648      */
11649     queryParam: 'query',
11650     /**
11651      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11652      * when mode = 'remote' (defaults to 'Loading...')
11653      */
11654     loadingText: 'Loading...',
11655     /**
11656      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11657      */
11658     resizable: false,
11659     /**
11660      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11661      */
11662     handleHeight : 8,
11663     /**
11664      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11665      * traditional select (defaults to true)
11666      */
11667     editable: true,
11668     /**
11669      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11670      */
11671     allQuery: '',
11672     /**
11673      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11674      */
11675     mode: 'remote',
11676     /**
11677      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11678      * listWidth has a higher value)
11679      */
11680     minListWidth : 70,
11681     /**
11682      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11683      * allow the user to set arbitrary text into the field (defaults to false)
11684      */
11685     forceSelection:false,
11686     /**
11687      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11688      * if typeAhead = true (defaults to 250)
11689      */
11690     typeAheadDelay : 250,
11691     /**
11692      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11693      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11694      */
11695     valueNotFoundText : undefined,
11696     /**
11697      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11698      */
11699     blockFocus : false,
11700     
11701     /**
11702      * @cfg {Boolean} disableClear Disable showing of clear button.
11703      */
11704     disableClear : false,
11705     /**
11706      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11707      */
11708     alwaysQuery : false,
11709     
11710     /**
11711      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11712      */
11713     multiple : false,
11714     
11715     /**
11716      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11717      */
11718     invalidClass : "has-warning",
11719     
11720     /**
11721      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11722      */
11723     validClass : "has-success",
11724     
11725     /**
11726      * @cfg {Boolean} specialFilter (true|false) special filter default false
11727      */
11728     specialFilter : false,
11729     
11730     /**
11731      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11732      */
11733     mobileTouchView : true,
11734     
11735     //private
11736     addicon : false,
11737     editicon: false,
11738     
11739     page: 0,
11740     hasQuery: false,
11741     append: false,
11742     loadNext: false,
11743     autoFocus : true,
11744     tickable : false,
11745     btnPosition : 'right',
11746     triggerList : true,
11747     showToggleBtn : true,
11748     animate : true,
11749     emptyResultText: 'Empty',
11750     triggerText : 'Select',
11751     
11752     // element that contains real text value.. (when hidden is used..)
11753     
11754     getAutoCreate : function()
11755     {
11756         var cfg = false;
11757         
11758         /*
11759          * Touch Devices
11760          */
11761         
11762         if(Roo.isTouch && this.mobileTouchView){
11763             cfg = this.getAutoCreateTouchView();
11764             return cfg;;
11765         }
11766         
11767         /*
11768          *  Normal ComboBox
11769          */
11770         if(!this.tickable){
11771             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11772             return cfg;
11773         }
11774         
11775         /*
11776          *  ComboBox with tickable selections
11777          */
11778              
11779         var align = this.labelAlign || this.parentLabelAlign();
11780         
11781         cfg = {
11782             cls : 'form-group roo-combobox-tickable' //input-group
11783         };
11784         
11785         var buttons = {
11786             tag : 'div',
11787             cls : 'tickable-buttons',
11788             cn : [
11789                 {
11790                     tag : 'button',
11791                     type : 'button',
11792                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11793                     html : this.triggerText
11794                 },
11795                 {
11796                     tag : 'button',
11797                     type : 'button',
11798                     name : 'ok',
11799                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11800                     html : 'Done'
11801                 },
11802                 {
11803                     tag : 'button',
11804                     type : 'button',
11805                     name : 'cancel',
11806                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11807                     html : 'Cancel'
11808                 }
11809             ]
11810         };
11811         
11812         if(this.editable){
11813             buttons.cn.unshift({
11814                 tag: 'input',
11815                 cls: 'select2-search-field-input'
11816             });
11817         }
11818         
11819         var _this = this;
11820         
11821         Roo.each(buttons.cn, function(c){
11822             if (_this.size) {
11823                 c.cls += ' btn-' + _this.size;
11824             }
11825
11826             if (_this.disabled) {
11827                 c.disabled = true;
11828             }
11829         });
11830         
11831         var box = {
11832             tag: 'div',
11833             cn: [
11834                 {
11835                     tag: 'input',
11836                     type : 'hidden',
11837                     cls: 'form-hidden-field'
11838                 },
11839                 {
11840                     tag: 'ul',
11841                     cls: 'select2-choices',
11842                     cn:[
11843                         {
11844                             tag: 'li',
11845                             cls: 'select2-search-field',
11846                             cn: [
11847
11848                                 buttons
11849                             ]
11850                         }
11851                     ]
11852                 }
11853             ]
11854         };
11855         
11856         var combobox = {
11857             cls: 'select2-container input-group select2-container-multi',
11858             cn: [
11859                 box
11860 //                {
11861 //                    tag: 'ul',
11862 //                    cls: 'typeahead typeahead-long dropdown-menu',
11863 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11864 //                }
11865             ]
11866         };
11867         
11868         if(this.hasFeedback && !this.allowBlank){
11869             
11870             var feedback = {
11871                 tag: 'span',
11872                 cls: 'glyphicon form-control-feedback'
11873             };
11874
11875             combobox.cn.push(feedback);
11876         }
11877         
11878         if (align ==='left' && this.fieldLabel.length) {
11879             
11880                 Roo.log("left and has label");
11881                 cfg.cn = [
11882                     
11883                     {
11884                         tag: 'label',
11885                         'for' :  id,
11886                         cls : 'control-label col-sm-' + this.labelWidth,
11887                         html : this.fieldLabel
11888                         
11889                     },
11890                     {
11891                         cls : "col-sm-" + (12 - this.labelWidth), 
11892                         cn: [
11893                             combobox
11894                         ]
11895                     }
11896                     
11897                 ];
11898         } else if ( this.fieldLabel.length) {
11899                 Roo.log(" label");
11900                  cfg.cn = [
11901                    
11902                     {
11903                         tag: 'label',
11904                         //cls : 'input-group-addon',
11905                         html : this.fieldLabel
11906                         
11907                     },
11908                     
11909                     combobox
11910                     
11911                 ];
11912
11913         } else {
11914             
11915                 Roo.log(" no label && no align");
11916                 cfg = combobox
11917                      
11918                 
11919         }
11920          
11921         var settings=this;
11922         ['xs','sm','md','lg'].map(function(size){
11923             if (settings[size]) {
11924                 cfg.cls += ' col-' + size + '-' + settings[size];
11925             }
11926         });
11927         
11928         return cfg;
11929         
11930     },
11931     
11932     _initEventsCalled : false,
11933     
11934     // private
11935     initEvents: function()
11936     {
11937         
11938         if (this._initEventsCalled) { // as we call render... prevent looping...
11939             return;
11940         }
11941         this._initEventsCalled = true;
11942         
11943         if (!this.store) {
11944             throw "can not find store for combo";
11945         }
11946         
11947         this.store = Roo.factory(this.store, Roo.data);
11948         
11949         // if we are building from html. then this element is so complex, that we can not really
11950         // use the rendered HTML.
11951         // so we have to trash and replace the previous code.
11952         if (Roo.XComponent.build_from_html) {
11953             
11954             // remove this element....
11955             var e = this.el.dom, k=0;
11956             while (e ) { e = e.previousSibling;  ++k;}
11957
11958             this.el.remove();
11959             
11960             this.el=false;
11961             this.rendered = false;
11962             
11963             this.render(this.parent().getChildContainer(true), k);
11964             
11965             
11966             
11967         }
11968         
11969         
11970         /*
11971          * Touch Devices
11972          */
11973         
11974         if(Roo.isTouch && this.mobileTouchView){
11975             this.initTouchView();
11976             return;
11977         }
11978         
11979         if(this.tickable){
11980             this.initTickableEvents();
11981             return;
11982         }
11983         
11984         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11985         
11986         if(this.hiddenName){
11987             
11988             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11989             
11990             this.hiddenField.dom.value =
11991                 this.hiddenValue !== undefined ? this.hiddenValue :
11992                 this.value !== undefined ? this.value : '';
11993
11994             // prevent input submission
11995             this.el.dom.removeAttribute('name');
11996             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11997              
11998              
11999         }
12000         //if(Roo.isGecko){
12001         //    this.el.dom.setAttribute('autocomplete', 'off');
12002         //}
12003         
12004         var cls = 'x-combo-list';
12005         
12006         //this.list = new Roo.Layer({
12007         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12008         //});
12009         
12010         var _this = this;
12011         
12012         (function(){
12013             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12014             _this.list.setWidth(lw);
12015         }).defer(100);
12016         
12017         this.list.on('mouseover', this.onViewOver, this);
12018         this.list.on('mousemove', this.onViewMove, this);
12019         
12020         this.list.on('scroll', this.onViewScroll, this);
12021         
12022         /*
12023         this.list.swallowEvent('mousewheel');
12024         this.assetHeight = 0;
12025
12026         if(this.title){
12027             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12028             this.assetHeight += this.header.getHeight();
12029         }
12030
12031         this.innerList = this.list.createChild({cls:cls+'-inner'});
12032         this.innerList.on('mouseover', this.onViewOver, this);
12033         this.innerList.on('mousemove', this.onViewMove, this);
12034         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12035         
12036         if(this.allowBlank && !this.pageSize && !this.disableClear){
12037             this.footer = this.list.createChild({cls:cls+'-ft'});
12038             this.pageTb = new Roo.Toolbar(this.footer);
12039            
12040         }
12041         if(this.pageSize){
12042             this.footer = this.list.createChild({cls:cls+'-ft'});
12043             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12044                     {pageSize: this.pageSize});
12045             
12046         }
12047         
12048         if (this.pageTb && this.allowBlank && !this.disableClear) {
12049             var _this = this;
12050             this.pageTb.add(new Roo.Toolbar.Fill(), {
12051                 cls: 'x-btn-icon x-btn-clear',
12052                 text: '&#160;',
12053                 handler: function()
12054                 {
12055                     _this.collapse();
12056                     _this.clearValue();
12057                     _this.onSelect(false, -1);
12058                 }
12059             });
12060         }
12061         if (this.footer) {
12062             this.assetHeight += this.footer.getHeight();
12063         }
12064         */
12065             
12066         if(!this.tpl){
12067             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12068         }
12069
12070         this.view = new Roo.View(this.list, this.tpl, {
12071             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12072         });
12073         //this.view.wrapEl.setDisplayed(false);
12074         this.view.on('click', this.onViewClick, this);
12075         
12076         
12077         
12078         this.store.on('beforeload', this.onBeforeLoad, this);
12079         this.store.on('load', this.onLoad, this);
12080         this.store.on('loadexception', this.onLoadException, this);
12081         /*
12082         if(this.resizable){
12083             this.resizer = new Roo.Resizable(this.list,  {
12084                pinned:true, handles:'se'
12085             });
12086             this.resizer.on('resize', function(r, w, h){
12087                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12088                 this.listWidth = w;
12089                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12090                 this.restrictHeight();
12091             }, this);
12092             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12093         }
12094         */
12095         if(!this.editable){
12096             this.editable = true;
12097             this.setEditable(false);
12098         }
12099         
12100         /*
12101         
12102         if (typeof(this.events.add.listeners) != 'undefined') {
12103             
12104             this.addicon = this.wrap.createChild(
12105                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12106        
12107             this.addicon.on('click', function(e) {
12108                 this.fireEvent('add', this);
12109             }, this);
12110         }
12111         if (typeof(this.events.edit.listeners) != 'undefined') {
12112             
12113             this.editicon = this.wrap.createChild(
12114                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12115             if (this.addicon) {
12116                 this.editicon.setStyle('margin-left', '40px');
12117             }
12118             this.editicon.on('click', function(e) {
12119                 
12120                 // we fire even  if inothing is selected..
12121                 this.fireEvent('edit', this, this.lastData );
12122                 
12123             }, this);
12124         }
12125         */
12126         
12127         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12128             "up" : function(e){
12129                 this.inKeyMode = true;
12130                 this.selectPrev();
12131             },
12132
12133             "down" : function(e){
12134                 if(!this.isExpanded()){
12135                     this.onTriggerClick();
12136                 }else{
12137                     this.inKeyMode = true;
12138                     this.selectNext();
12139                 }
12140             },
12141
12142             "enter" : function(e){
12143 //                this.onViewClick();
12144                 //return true;
12145                 this.collapse();
12146                 
12147                 if(this.fireEvent("specialkey", this, e)){
12148                     this.onViewClick(false);
12149                 }
12150                 
12151                 return true;
12152             },
12153
12154             "esc" : function(e){
12155                 this.collapse();
12156             },
12157
12158             "tab" : function(e){
12159                 this.collapse();
12160                 
12161                 if(this.fireEvent("specialkey", this, e)){
12162                     this.onViewClick(false);
12163                 }
12164                 
12165                 return true;
12166             },
12167
12168             scope : this,
12169
12170             doRelay : function(foo, bar, hname){
12171                 if(hname == 'down' || this.scope.isExpanded()){
12172                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12173                 }
12174                 return true;
12175             },
12176
12177             forceKeyDown: true
12178         });
12179         
12180         
12181         this.queryDelay = Math.max(this.queryDelay || 10,
12182                 this.mode == 'local' ? 10 : 250);
12183         
12184         
12185         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12186         
12187         if(this.typeAhead){
12188             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12189         }
12190         if(this.editable !== false){
12191             this.inputEl().on("keyup", this.onKeyUp, this);
12192         }
12193         if(this.forceSelection){
12194             this.inputEl().on('blur', this.doForce, this);
12195         }
12196         
12197         if(this.multiple){
12198             this.choices = this.el.select('ul.select2-choices', true).first();
12199             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12200         }
12201     },
12202     
12203     initTickableEvents: function()
12204     {   
12205         this.createList();
12206         
12207         if(this.hiddenName){
12208             
12209             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12210             
12211             this.hiddenField.dom.value =
12212                 this.hiddenValue !== undefined ? this.hiddenValue :
12213                 this.value !== undefined ? this.value : '';
12214
12215             // prevent input submission
12216             this.el.dom.removeAttribute('name');
12217             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12218              
12219              
12220         }
12221         
12222 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12223         
12224         this.choices = this.el.select('ul.select2-choices', true).first();
12225         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12226         if(this.triggerList){
12227             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12228         }
12229          
12230         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12231         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12232         
12233         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12234         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12235         
12236         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12237         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12238         
12239         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12240         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12241         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12242         
12243         this.okBtn.hide();
12244         this.cancelBtn.hide();
12245         
12246         var _this = this;
12247         
12248         (function(){
12249             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12250             _this.list.setWidth(lw);
12251         }).defer(100);
12252         
12253         this.list.on('mouseover', this.onViewOver, this);
12254         this.list.on('mousemove', this.onViewMove, this);
12255         
12256         this.list.on('scroll', this.onViewScroll, this);
12257         
12258         if(!this.tpl){
12259             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>';
12260         }
12261
12262         this.view = new Roo.View(this.list, this.tpl, {
12263             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12264         });
12265         
12266         //this.view.wrapEl.setDisplayed(false);
12267         this.view.on('click', this.onViewClick, this);
12268         
12269         
12270         
12271         this.store.on('beforeload', this.onBeforeLoad, this);
12272         this.store.on('load', this.onLoad, this);
12273         this.store.on('loadexception', this.onLoadException, this);
12274         
12275         if(this.editable){
12276             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12277                 "up" : function(e){
12278                     this.inKeyMode = true;
12279                     this.selectPrev();
12280                 },
12281
12282                 "down" : function(e){
12283                     this.inKeyMode = true;
12284                     this.selectNext();
12285                 },
12286
12287                 "enter" : function(e){
12288                     if(this.fireEvent("specialkey", this, e)){
12289                         this.onViewClick(false);
12290                     }
12291                     
12292                     return true;
12293                 },
12294
12295                 "esc" : function(e){
12296                     this.onTickableFooterButtonClick(e, false, false);
12297                 },
12298
12299                 "tab" : function(e){
12300                     this.fireEvent("specialkey", this, e);
12301                     
12302                     this.onTickableFooterButtonClick(e, false, false);
12303                     
12304                     return true;
12305                 },
12306
12307                 scope : this,
12308
12309                 doRelay : function(e, fn, key){
12310                     if(this.scope.isExpanded()){
12311                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12312                     }
12313                     return true;
12314                 },
12315
12316                 forceKeyDown: true
12317             });
12318         }
12319         
12320         this.queryDelay = Math.max(this.queryDelay || 10,
12321                 this.mode == 'local' ? 10 : 250);
12322         
12323         
12324         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12325         
12326         if(this.typeAhead){
12327             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12328         }
12329         
12330         if(this.editable !== false){
12331             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12332         }
12333         
12334     },
12335
12336     onDestroy : function(){
12337         if(this.view){
12338             this.view.setStore(null);
12339             this.view.el.removeAllListeners();
12340             this.view.el.remove();
12341             this.view.purgeListeners();
12342         }
12343         if(this.list){
12344             this.list.dom.innerHTML  = '';
12345         }
12346         
12347         if(this.store){
12348             this.store.un('beforeload', this.onBeforeLoad, this);
12349             this.store.un('load', this.onLoad, this);
12350             this.store.un('loadexception', this.onLoadException, this);
12351         }
12352         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12353     },
12354
12355     // private
12356     fireKey : function(e){
12357         if(e.isNavKeyPress() && !this.list.isVisible()){
12358             this.fireEvent("specialkey", this, e);
12359         }
12360     },
12361
12362     // private
12363     onResize: function(w, h){
12364 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12365 //        
12366 //        if(typeof w != 'number'){
12367 //            // we do not handle it!?!?
12368 //            return;
12369 //        }
12370 //        var tw = this.trigger.getWidth();
12371 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12372 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12373 //        var x = w - tw;
12374 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12375 //            
12376 //        //this.trigger.setStyle('left', x+'px');
12377 //        
12378 //        if(this.list && this.listWidth === undefined){
12379 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12380 //            this.list.setWidth(lw);
12381 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12382 //        }
12383         
12384     
12385         
12386     },
12387
12388     /**
12389      * Allow or prevent the user from directly editing the field text.  If false is passed,
12390      * the user will only be able to select from the items defined in the dropdown list.  This method
12391      * is the runtime equivalent of setting the 'editable' config option at config time.
12392      * @param {Boolean} value True to allow the user to directly edit the field text
12393      */
12394     setEditable : function(value){
12395         if(value == this.editable){
12396             return;
12397         }
12398         this.editable = value;
12399         if(!value){
12400             this.inputEl().dom.setAttribute('readOnly', true);
12401             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12402             this.inputEl().addClass('x-combo-noedit');
12403         }else{
12404             this.inputEl().dom.setAttribute('readOnly', false);
12405             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12406             this.inputEl().removeClass('x-combo-noedit');
12407         }
12408     },
12409
12410     // private
12411     
12412     onBeforeLoad : function(combo,opts){
12413         if(!this.hasFocus){
12414             return;
12415         }
12416          if (!opts.add) {
12417             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12418          }
12419         this.restrictHeight();
12420         this.selectedIndex = -1;
12421     },
12422
12423     // private
12424     onLoad : function(){
12425         
12426         this.hasQuery = false;
12427         
12428         if(!this.hasFocus){
12429             return;
12430         }
12431         
12432         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12433             this.loading.hide();
12434         }
12435              
12436         if(this.store.getCount() > 0){
12437             this.expand();
12438             this.restrictHeight();
12439             if(this.lastQuery == this.allQuery){
12440                 if(this.editable && !this.tickable){
12441                     this.inputEl().dom.select();
12442                 }
12443                 
12444                 if(
12445                     !this.selectByValue(this.value, true) &&
12446                     this.autoFocus && 
12447                     (
12448                         !this.store.lastOptions ||
12449                         typeof(this.store.lastOptions.add) == 'undefined' || 
12450                         this.store.lastOptions.add != true
12451                     )
12452                 ){
12453                     this.select(0, true);
12454                 }
12455             }else{
12456                 if(this.autoFocus){
12457                     this.selectNext();
12458                 }
12459                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12460                     this.taTask.delay(this.typeAheadDelay);
12461                 }
12462             }
12463         }else{
12464             this.onEmptyResults();
12465         }
12466         
12467         //this.el.focus();
12468     },
12469     // private
12470     onLoadException : function()
12471     {
12472         this.hasQuery = false;
12473         
12474         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12475             this.loading.hide();
12476         }
12477         
12478         if(this.tickable && this.editable){
12479             return;
12480         }
12481         
12482         this.collapse();
12483         // only causes errors at present
12484         //Roo.log(this.store.reader.jsonData);
12485         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12486             // fixme
12487             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12488         //}
12489         
12490         
12491     },
12492     // private
12493     onTypeAhead : function(){
12494         if(this.store.getCount() > 0){
12495             var r = this.store.getAt(0);
12496             var newValue = r.data[this.displayField];
12497             var len = newValue.length;
12498             var selStart = this.getRawValue().length;
12499             
12500             if(selStart != len){
12501                 this.setRawValue(newValue);
12502                 this.selectText(selStart, newValue.length);
12503             }
12504         }
12505     },
12506
12507     // private
12508     onSelect : function(record, index){
12509         
12510         if(this.fireEvent('beforeselect', this, record, index) !== false){
12511         
12512             this.setFromData(index > -1 ? record.data : false);
12513             
12514             this.collapse();
12515             this.fireEvent('select', this, record, index);
12516         }
12517     },
12518
12519     /**
12520      * Returns the currently selected field value or empty string if no value is set.
12521      * @return {String} value The selected value
12522      */
12523     getValue : function(){
12524         
12525         if(this.multiple){
12526             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12527         }
12528         
12529         if(this.valueField){
12530             return typeof this.value != 'undefined' ? this.value : '';
12531         }else{
12532             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12533         }
12534     },
12535
12536     /**
12537      * Clears any text/value currently set in the field
12538      */
12539     clearValue : function(){
12540         if(this.hiddenField){
12541             this.hiddenField.dom.value = '';
12542         }
12543         this.value = '';
12544         this.setRawValue('');
12545         this.lastSelectionText = '';
12546         this.lastData = false;
12547         
12548         var close = this.closeTriggerEl();
12549         
12550         if(close){
12551             close.hide();
12552         }
12553         
12554     },
12555
12556     /**
12557      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12558      * will be displayed in the field.  If the value does not match the data value of an existing item,
12559      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12560      * Otherwise the field will be blank (although the value will still be set).
12561      * @param {String} value The value to match
12562      */
12563     setValue : function(v){
12564         if(this.multiple){
12565             this.syncValue();
12566             return;
12567         }
12568         
12569         var text = v;
12570         if(this.valueField){
12571             var r = this.findRecord(this.valueField, v);
12572             if(r){
12573                 text = r.data[this.displayField];
12574             }else if(this.valueNotFoundText !== undefined){
12575                 text = this.valueNotFoundText;
12576             }
12577         }
12578         this.lastSelectionText = text;
12579         if(this.hiddenField){
12580             this.hiddenField.dom.value = v;
12581         }
12582         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12583         this.value = v;
12584         
12585         var close = this.closeTriggerEl();
12586         
12587         if(close){
12588             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12589         }
12590     },
12591     /**
12592      * @property {Object} the last set data for the element
12593      */
12594     
12595     lastData : false,
12596     /**
12597      * Sets the value of the field based on a object which is related to the record format for the store.
12598      * @param {Object} value the value to set as. or false on reset?
12599      */
12600     setFromData : function(o){
12601         
12602         if(this.multiple){
12603             this.addItem(o);
12604             return;
12605         }
12606             
12607         var dv = ''; // display value
12608         var vv = ''; // value value..
12609         this.lastData = o;
12610         if (this.displayField) {
12611             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12612         } else {
12613             // this is an error condition!!!
12614             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12615         }
12616         
12617         if(this.valueField){
12618             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12619         }
12620         
12621         var close = this.closeTriggerEl();
12622         
12623         if(close){
12624             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12625         }
12626         
12627         if(this.hiddenField){
12628             this.hiddenField.dom.value = vv;
12629             
12630             this.lastSelectionText = dv;
12631             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12632             this.value = vv;
12633             return;
12634         }
12635         // no hidden field.. - we store the value in 'value', but still display
12636         // display field!!!!
12637         this.lastSelectionText = dv;
12638         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12639         this.value = vv;
12640         
12641         
12642         
12643     },
12644     // private
12645     reset : function(){
12646         // overridden so that last data is reset..
12647         
12648         if(this.multiple){
12649             this.clearItem();
12650             return;
12651         }
12652         
12653         this.setValue(this.originalValue);
12654         this.clearInvalid();
12655         this.lastData = false;
12656         if (this.view) {
12657             this.view.clearSelections();
12658         }
12659     },
12660     // private
12661     findRecord : function(prop, value){
12662         var record;
12663         if(this.store.getCount() > 0){
12664             this.store.each(function(r){
12665                 if(r.data[prop] == value){
12666                     record = r;
12667                     return false;
12668                 }
12669                 return true;
12670             });
12671         }
12672         return record;
12673     },
12674     
12675     getName: function()
12676     {
12677         // returns hidden if it's set..
12678         if (!this.rendered) {return ''};
12679         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12680         
12681     },
12682     // private
12683     onViewMove : function(e, t){
12684         this.inKeyMode = false;
12685     },
12686
12687     // private
12688     onViewOver : function(e, t){
12689         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12690             return;
12691         }
12692         var item = this.view.findItemFromChild(t);
12693         
12694         if(item){
12695             var index = this.view.indexOf(item);
12696             this.select(index, false);
12697         }
12698     },
12699
12700     // private
12701     onViewClick : function(view, doFocus, el, e)
12702     {
12703         var index = this.view.getSelectedIndexes()[0];
12704         
12705         var r = this.store.getAt(index);
12706         
12707         if(this.tickable){
12708             
12709             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12710                 return;
12711             }
12712             
12713             var rm = false;
12714             var _this = this;
12715             
12716             Roo.each(this.tickItems, function(v,k){
12717                 
12718                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12719                     Roo.log(v);
12720                     _this.tickItems.splice(k, 1);
12721                     
12722                     if(typeof(e) == 'undefined' && view == false){
12723                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12724                     }
12725                     
12726                     rm = true;
12727                     return;
12728                 }
12729             });
12730             
12731             if(rm){
12732                 return;
12733             }
12734             
12735             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12736                 this.tickItems.push(r.data);
12737             }
12738             
12739             if(typeof(e) == 'undefined' && view == false){
12740                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12741             }
12742                     
12743             return;
12744         }
12745         
12746         if(r){
12747             this.onSelect(r, index);
12748         }
12749         if(doFocus !== false && !this.blockFocus){
12750             this.inputEl().focus();
12751         }
12752     },
12753
12754     // private
12755     restrictHeight : function(){
12756         //this.innerList.dom.style.height = '';
12757         //var inner = this.innerList.dom;
12758         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12759         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12760         //this.list.beginUpdate();
12761         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12762         this.list.alignTo(this.inputEl(), this.listAlign);
12763         this.list.alignTo(this.inputEl(), this.listAlign);
12764         //this.list.endUpdate();
12765     },
12766
12767     // private
12768     onEmptyResults : function(){
12769         
12770         if(this.tickable && this.editable){
12771             this.restrictHeight();
12772             return;
12773         }
12774         
12775         this.collapse();
12776     },
12777
12778     /**
12779      * Returns true if the dropdown list is expanded, else false.
12780      */
12781     isExpanded : function(){
12782         return this.list.isVisible();
12783     },
12784
12785     /**
12786      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12787      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12788      * @param {String} value The data value of the item to select
12789      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12790      * selected item if it is not currently in view (defaults to true)
12791      * @return {Boolean} True if the value matched an item in the list, else false
12792      */
12793     selectByValue : function(v, scrollIntoView){
12794         if(v !== undefined && v !== null){
12795             var r = this.findRecord(this.valueField || this.displayField, v);
12796             if(r){
12797                 this.select(this.store.indexOf(r), scrollIntoView);
12798                 return true;
12799             }
12800         }
12801         return false;
12802     },
12803
12804     /**
12805      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12806      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12807      * @param {Number} index The zero-based index of the list item to select
12808      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12809      * selected item if it is not currently in view (defaults to true)
12810      */
12811     select : function(index, scrollIntoView){
12812         this.selectedIndex = index;
12813         this.view.select(index);
12814         if(scrollIntoView !== false){
12815             var el = this.view.getNode(index);
12816             /*
12817              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12818              */
12819             if(el){
12820                 this.list.scrollChildIntoView(el, false);
12821             }
12822         }
12823     },
12824
12825     // private
12826     selectNext : function(){
12827         var ct = this.store.getCount();
12828         if(ct > 0){
12829             if(this.selectedIndex == -1){
12830                 this.select(0);
12831             }else if(this.selectedIndex < ct-1){
12832                 this.select(this.selectedIndex+1);
12833             }
12834         }
12835     },
12836
12837     // private
12838     selectPrev : function(){
12839         var ct = this.store.getCount();
12840         if(ct > 0){
12841             if(this.selectedIndex == -1){
12842                 this.select(0);
12843             }else if(this.selectedIndex != 0){
12844                 this.select(this.selectedIndex-1);
12845             }
12846         }
12847     },
12848
12849     // private
12850     onKeyUp : function(e){
12851         if(this.editable !== false && !e.isSpecialKey()){
12852             this.lastKey = e.getKey();
12853             this.dqTask.delay(this.queryDelay);
12854         }
12855     },
12856
12857     // private
12858     validateBlur : function(){
12859         return !this.list || !this.list.isVisible();   
12860     },
12861
12862     // private
12863     initQuery : function(){
12864         
12865         var v = this.getRawValue();
12866         
12867         if(this.tickable && this.editable){
12868             v = this.tickableInputEl().getValue();
12869         }
12870         
12871         this.doQuery(v);
12872     },
12873
12874     // private
12875     doForce : function(){
12876         if(this.inputEl().dom.value.length > 0){
12877             this.inputEl().dom.value =
12878                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12879              
12880         }
12881     },
12882
12883     /**
12884      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12885      * query allowing the query action to be canceled if needed.
12886      * @param {String} query The SQL query to execute
12887      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12888      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12889      * saved in the current store (defaults to false)
12890      */
12891     doQuery : function(q, forceAll){
12892         
12893         if(q === undefined || q === null){
12894             q = '';
12895         }
12896         var qe = {
12897             query: q,
12898             forceAll: forceAll,
12899             combo: this,
12900             cancel:false
12901         };
12902         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12903             return false;
12904         }
12905         q = qe.query;
12906         
12907         forceAll = qe.forceAll;
12908         if(forceAll === true || (q.length >= this.minChars)){
12909             
12910             this.hasQuery = true;
12911             
12912             if(this.lastQuery != q || this.alwaysQuery){
12913                 this.lastQuery = q;
12914                 if(this.mode == 'local'){
12915                     this.selectedIndex = -1;
12916                     if(forceAll){
12917                         this.store.clearFilter();
12918                     }else{
12919                         
12920                         if(this.specialFilter){
12921                             this.fireEvent('specialfilter', this);
12922                             this.onLoad();
12923                             return;
12924                         }
12925                         
12926                         this.store.filter(this.displayField, q);
12927                     }
12928                     
12929                     this.store.fireEvent("datachanged", this.store);
12930                     
12931                     this.onLoad();
12932                     
12933                     
12934                 }else{
12935                     
12936                     this.store.baseParams[this.queryParam] = q;
12937                     
12938                     var options = {params : this.getParams(q)};
12939                     
12940                     if(this.loadNext){
12941                         options.add = true;
12942                         options.params.start = this.page * this.pageSize;
12943                     }
12944                     
12945                     this.store.load(options);
12946                     
12947                     /*
12948                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12949                      *  we should expand the list on onLoad
12950                      *  so command out it
12951                      */
12952 //                    this.expand();
12953                 }
12954             }else{
12955                 this.selectedIndex = -1;
12956                 this.onLoad();   
12957             }
12958         }
12959         
12960         this.loadNext = false;
12961     },
12962     
12963     // private
12964     getParams : function(q){
12965         var p = {};
12966         //p[this.queryParam] = q;
12967         
12968         if(this.pageSize){
12969             p.start = 0;
12970             p.limit = this.pageSize;
12971         }
12972         return p;
12973     },
12974
12975     /**
12976      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12977      */
12978     collapse : function(){
12979         if(!this.isExpanded()){
12980             return;
12981         }
12982         
12983         this.list.hide();
12984         
12985         if(this.tickable){
12986             this.hasFocus = false;
12987             this.okBtn.hide();
12988             this.cancelBtn.hide();
12989             this.trigger.show();
12990             
12991             if(this.editable){
12992                 this.tickableInputEl().dom.value = '';
12993                 this.tickableInputEl().blur();
12994             }
12995             
12996         }
12997         
12998         Roo.get(document).un('mousedown', this.collapseIf, this);
12999         Roo.get(document).un('mousewheel', this.collapseIf, this);
13000         if (!this.editable) {
13001             Roo.get(document).un('keydown', this.listKeyPress, this);
13002         }
13003         this.fireEvent('collapse', this);
13004     },
13005
13006     // private
13007     collapseIf : function(e){
13008         var in_combo  = e.within(this.el);
13009         var in_list =  e.within(this.list);
13010         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13011         
13012         if (in_combo || in_list || is_list) {
13013             //e.stopPropagation();
13014             return;
13015         }
13016         
13017         if(this.tickable){
13018             this.onTickableFooterButtonClick(e, false, false);
13019         }
13020
13021         this.collapse();
13022         
13023     },
13024
13025     /**
13026      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13027      */
13028     expand : function(){
13029        
13030         if(this.isExpanded() || !this.hasFocus){
13031             return;
13032         }
13033         
13034         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13035         this.list.setWidth(lw);
13036         
13037         
13038          Roo.log('expand');
13039         
13040         this.list.show();
13041         
13042         this.restrictHeight();
13043         
13044         if(this.tickable){
13045             
13046             this.tickItems = Roo.apply([], this.item);
13047             
13048             this.okBtn.show();
13049             this.cancelBtn.show();
13050             this.trigger.hide();
13051             
13052             if(this.editable){
13053                 this.tickableInputEl().focus();
13054             }
13055             
13056         }
13057         
13058         Roo.get(document).on('mousedown', this.collapseIf, this);
13059         Roo.get(document).on('mousewheel', this.collapseIf, this);
13060         if (!this.editable) {
13061             Roo.get(document).on('keydown', this.listKeyPress, this);
13062         }
13063         
13064         this.fireEvent('expand', this);
13065     },
13066
13067     // private
13068     // Implements the default empty TriggerField.onTriggerClick function
13069     onTriggerClick : function(e)
13070     {
13071         Roo.log('trigger click');
13072         
13073         if(this.disabled || !this.triggerList){
13074             return;
13075         }
13076         
13077         this.page = 0;
13078         this.loadNext = false;
13079         
13080         if(this.isExpanded()){
13081             this.collapse();
13082             if (!this.blockFocus) {
13083                 this.inputEl().focus();
13084             }
13085             
13086         }else {
13087             this.hasFocus = true;
13088             if(this.triggerAction == 'all') {
13089                 this.doQuery(this.allQuery, true);
13090             } else {
13091                 this.doQuery(this.getRawValue());
13092             }
13093             if (!this.blockFocus) {
13094                 this.inputEl().focus();
13095             }
13096         }
13097     },
13098     
13099     onTickableTriggerClick : function(e)
13100     {
13101         if(this.disabled){
13102             return;
13103         }
13104         
13105         this.page = 0;
13106         this.loadNext = false;
13107         this.hasFocus = true;
13108         
13109         if(this.triggerAction == 'all') {
13110             this.doQuery(this.allQuery, true);
13111         } else {
13112             this.doQuery(this.getRawValue());
13113         }
13114     },
13115     
13116     onSearchFieldClick : function(e)
13117     {
13118         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13119             this.onTickableFooterButtonClick(e, false, false);
13120             return;
13121         }
13122         
13123         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13124             return;
13125         }
13126         
13127         this.page = 0;
13128         this.loadNext = false;
13129         this.hasFocus = true;
13130         
13131         if(this.triggerAction == 'all') {
13132             this.doQuery(this.allQuery, true);
13133         } else {
13134             this.doQuery(this.getRawValue());
13135         }
13136     },
13137     
13138     listKeyPress : function(e)
13139     {
13140         //Roo.log('listkeypress');
13141         // scroll to first matching element based on key pres..
13142         if (e.isSpecialKey()) {
13143             return false;
13144         }
13145         var k = String.fromCharCode(e.getKey()).toUpperCase();
13146         //Roo.log(k);
13147         var match  = false;
13148         var csel = this.view.getSelectedNodes();
13149         var cselitem = false;
13150         if (csel.length) {
13151             var ix = this.view.indexOf(csel[0]);
13152             cselitem  = this.store.getAt(ix);
13153             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13154                 cselitem = false;
13155             }
13156             
13157         }
13158         
13159         this.store.each(function(v) { 
13160             if (cselitem) {
13161                 // start at existing selection.
13162                 if (cselitem.id == v.id) {
13163                     cselitem = false;
13164                 }
13165                 return true;
13166             }
13167                 
13168             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13169                 match = this.store.indexOf(v);
13170                 return false;
13171             }
13172             return true;
13173         }, this);
13174         
13175         if (match === false) {
13176             return true; // no more action?
13177         }
13178         // scroll to?
13179         this.view.select(match);
13180         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13181         sn.scrollIntoView(sn.dom.parentNode, false);
13182     },
13183     
13184     onViewScroll : function(e, t){
13185         
13186         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){
13187             return;
13188         }
13189         
13190         this.hasQuery = true;
13191         
13192         this.loading = this.list.select('.loading', true).first();
13193         
13194         if(this.loading === null){
13195             this.list.createChild({
13196                 tag: 'div',
13197                 cls: 'loading select2-more-results select2-active',
13198                 html: 'Loading more results...'
13199             });
13200             
13201             this.loading = this.list.select('.loading', true).first();
13202             
13203             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13204             
13205             this.loading.hide();
13206         }
13207         
13208         this.loading.show();
13209         
13210         var _combo = this;
13211         
13212         this.page++;
13213         this.loadNext = true;
13214         
13215         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13216         
13217         return;
13218     },
13219     
13220     addItem : function(o)
13221     {   
13222         var dv = ''; // display value
13223         
13224         if (this.displayField) {
13225             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13226         } else {
13227             // this is an error condition!!!
13228             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13229         }
13230         
13231         if(!dv.length){
13232             return;
13233         }
13234         
13235         var choice = this.choices.createChild({
13236             tag: 'li',
13237             cls: 'select2-search-choice',
13238             cn: [
13239                 {
13240                     tag: 'div',
13241                     html: dv
13242                 },
13243                 {
13244                     tag: 'a',
13245                     href: '#',
13246                     cls: 'select2-search-choice-close',
13247                     tabindex: '-1'
13248                 }
13249             ]
13250             
13251         }, this.searchField);
13252         
13253         var close = choice.select('a.select2-search-choice-close', true).first();
13254         
13255         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13256         
13257         this.item.push(o);
13258         
13259         this.lastData = o;
13260         
13261         this.syncValue();
13262         
13263         this.inputEl().dom.value = '';
13264         
13265         this.validate();
13266     },
13267     
13268     onRemoveItem : function(e, _self, o)
13269     {
13270         e.preventDefault();
13271         
13272         this.lastItem = Roo.apply([], this.item);
13273         
13274         var index = this.item.indexOf(o.data) * 1;
13275         
13276         if( index < 0){
13277             Roo.log('not this item?!');
13278             return;
13279         }
13280         
13281         this.item.splice(index, 1);
13282         o.item.remove();
13283         
13284         this.syncValue();
13285         
13286         this.fireEvent('remove', this, e);
13287         
13288         this.validate();
13289         
13290     },
13291     
13292     syncValue : function()
13293     {
13294         if(!this.item.length){
13295             this.clearValue();
13296             return;
13297         }
13298             
13299         var value = [];
13300         var _this = this;
13301         Roo.each(this.item, function(i){
13302             if(_this.valueField){
13303                 value.push(i[_this.valueField]);
13304                 return;
13305             }
13306
13307             value.push(i);
13308         });
13309
13310         this.value = value.join(',');
13311
13312         if(this.hiddenField){
13313             this.hiddenField.dom.value = this.value;
13314         }
13315         
13316         this.store.fireEvent("datachanged", this.store);
13317     },
13318     
13319     clearItem : function()
13320     {
13321         if(!this.multiple){
13322             return;
13323         }
13324         
13325         this.item = [];
13326         
13327         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13328            c.remove();
13329         });
13330         
13331         this.syncValue();
13332         
13333         this.validate();
13334         
13335         if(this.tickable && !Roo.isTouch){
13336             this.view.refresh();
13337         }
13338     },
13339     
13340     inputEl: function ()
13341     {
13342         if(Roo.isTouch && this.mobileTouchView){
13343             return this.el.select('input.form-control',true).first();
13344         }
13345         
13346         if(this.tickable){
13347             return this.searchField;
13348         }
13349         
13350         return this.el.select('input.form-control',true).first();
13351     },
13352     
13353     
13354     onTickableFooterButtonClick : function(e, btn, el)
13355     {
13356         e.preventDefault();
13357         
13358         this.lastItem = Roo.apply([], this.item);
13359         
13360         if(btn && btn.name == 'cancel'){
13361             this.tickItems = Roo.apply([], this.item);
13362             this.collapse();
13363             return;
13364         }
13365         
13366         this.clearItem();
13367         
13368         var _this = this;
13369         
13370         Roo.each(this.tickItems, function(o){
13371             _this.addItem(o);
13372         });
13373         
13374         this.collapse();
13375         
13376     },
13377     
13378     validate : function()
13379     {
13380         var v = this.getRawValue();
13381         
13382         if(this.multiple){
13383             v = this.getValue();
13384         }
13385         
13386         if(this.disabled || this.allowBlank || v.length){
13387             this.markValid();
13388             return true;
13389         }
13390         
13391         this.markInvalid();
13392         return false;
13393     },
13394     
13395     tickableInputEl : function()
13396     {
13397         if(!this.tickable || !this.editable){
13398             return this.inputEl();
13399         }
13400         
13401         return this.inputEl().select('.select2-search-field-input', true).first();
13402     },
13403     
13404     
13405     getAutoCreateTouchView : function()
13406     {
13407         var id = Roo.id();
13408         
13409         var cfg = {
13410             cls: 'form-group' //input-group
13411         };
13412         
13413         var input =  {
13414             tag: 'input',
13415             id : id,
13416             type : this.inputType,
13417             cls : 'form-control x-combo-noedit',
13418             autocomplete: 'new-password',
13419             placeholder : this.placeholder || '',
13420             readonly : true
13421         };
13422         
13423         if (this.name) {
13424             input.name = this.name;
13425         }
13426         
13427         if (this.size) {
13428             input.cls += ' input-' + this.size;
13429         }
13430         
13431         if (this.disabled) {
13432             input.disabled = true;
13433         }
13434         
13435         var inputblock = {
13436             cls : '',
13437             cn : [
13438                 input
13439             ]
13440         };
13441         
13442         if(this.before){
13443             inputblock.cls += ' input-group';
13444             
13445             inputblock.cn.unshift({
13446                 tag :'span',
13447                 cls : 'input-group-addon',
13448                 html : this.before
13449             });
13450         }
13451         
13452         if(this.removable && !this.multiple){
13453             inputblock.cls += ' roo-removable';
13454             
13455             inputblock.cn.push({
13456                 tag: 'button',
13457                 html : 'x',
13458                 cls : 'roo-combo-removable-btn close'
13459             });
13460         }
13461
13462         if(this.hasFeedback && !this.allowBlank){
13463             
13464             inputblock.cls += ' has-feedback';
13465             
13466             inputblock.cn.push({
13467                 tag: 'span',
13468                 cls: 'glyphicon form-control-feedback'
13469             });
13470             
13471         }
13472         
13473         if (this.after) {
13474             
13475             inputblock.cls += (this.before) ? '' : ' input-group';
13476             
13477             inputblock.cn.push({
13478                 tag :'span',
13479                 cls : 'input-group-addon',
13480                 html : this.after
13481             });
13482         }
13483
13484         var box = {
13485             tag: 'div',
13486             cn: [
13487                 {
13488                     tag: 'input',
13489                     type : 'hidden',
13490                     cls: 'form-hidden-field'
13491                 },
13492                 inputblock
13493             ]
13494             
13495         };
13496         
13497         if(this.multiple){
13498             box = {
13499                 tag: 'div',
13500                 cn: [
13501                     {
13502                         tag: 'input',
13503                         type : 'hidden',
13504                         cls: 'form-hidden-field'
13505                     },
13506                     {
13507                         tag: 'ul',
13508                         cls: 'select2-choices',
13509                         cn:[
13510                             {
13511                                 tag: 'li',
13512                                 cls: 'select2-search-field',
13513                                 cn: [
13514
13515                                     inputblock
13516                                 ]
13517                             }
13518                         ]
13519                     }
13520                 ]
13521             }
13522         };
13523         
13524         var combobox = {
13525             cls: 'select2-container input-group',
13526             cn: [
13527                 box
13528             ]
13529         };
13530         
13531         if(this.multiple){
13532             combobox.cls += ' select2-container-multi';
13533         }
13534         
13535         var align = this.labelAlign || this.parentLabelAlign();
13536         
13537         cfg.cn = combobox;
13538         
13539         if(this.fieldLabel.length){
13540             
13541             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13542             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13543             
13544             cfg.cn = [
13545                 {
13546                     tag: 'label',
13547                     cls : 'control-label ' + lw,
13548                     html : this.fieldLabel
13549
13550                 },
13551                 {
13552                     cls : cw, 
13553                     cn: [
13554                         combobox
13555                     ]
13556                 }
13557             ];
13558         }
13559         
13560         var settings = this;
13561         
13562         ['xs','sm','md','lg'].map(function(size){
13563             if (settings[size]) {
13564                 cfg.cls += ' col-' + size + '-' + settings[size];
13565             }
13566         });
13567         
13568         return cfg;
13569     },
13570     
13571     initTouchView : function()
13572     {
13573         this.renderTouchView();
13574         
13575         this.touchViewEl.on('scroll', function(){
13576             this.el.dom.scrollTop = 0;
13577         }, this);
13578         
13579         this.inputEl().on("click", this.showTouchView, this);
13580         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13581         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13582         
13583         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13584         
13585         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13586         this.store.on('load', this.onTouchViewLoad, this);
13587         this.store.on('loadexception', this.onTouchViewLoadException, this);
13588         
13589         if(this.hiddenName){
13590             
13591             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13592             
13593             this.hiddenField.dom.value =
13594                 this.hiddenValue !== undefined ? this.hiddenValue :
13595                 this.value !== undefined ? this.value : '';
13596         
13597             this.el.dom.removeAttribute('name');
13598             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13599         }
13600         
13601         if(this.multiple){
13602             this.choices = this.el.select('ul.select2-choices', true).first();
13603             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13604         }
13605         
13606         if(this.removable && !this.multiple){
13607             var close = this.closeTriggerEl();
13608             if(close){
13609                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13610                 close.on('click', this.removeBtnClick, this, close);
13611             }
13612         }
13613         
13614         return;
13615         
13616         
13617     },
13618     
13619     renderTouchView : function()
13620     {
13621         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13622         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13623         
13624         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13625         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13626         
13627         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13628         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13629         this.touchViewBodyEl.setStyle('overflow', 'auto');
13630         
13631         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13632         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13633         
13634         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13635         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13636         
13637     },
13638     
13639     showTouchView : function()
13640     {
13641         this.touchViewHeaderEl.hide();
13642
13643         if(this.fieldLabel.length){
13644             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13645             this.touchViewHeaderEl.show();
13646         }
13647
13648         this.touchViewEl.show();
13649
13650         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13651         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13652
13653         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13654
13655         if(this.fieldLabel.length){
13656             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13657         }
13658         
13659         this.touchViewBodyEl.setHeight(bodyHeight);
13660
13661         if(this.animate){
13662             var _this = this;
13663             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13664         }else{
13665             this.touchViewEl.addClass('in');
13666         }
13667
13668         this.doTouchViewQuery();
13669         
13670     },
13671     
13672     hideTouchView : function()
13673     {
13674         this.touchViewEl.removeClass('in');
13675
13676         if(this.animate){
13677             var _this = this;
13678             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13679         }else{
13680             this.touchViewEl.setStyle('display', 'none');
13681         }
13682         
13683     },
13684     
13685     setTouchViewValue : function()
13686     {
13687         if(this.multiple){
13688             this.clearItem();
13689         
13690             var _this = this;
13691
13692             Roo.each(this.tickItems, function(o){
13693                 this.addItem(o);
13694             }, this);
13695         }
13696         
13697         this.hideTouchView();
13698     },
13699     
13700     doTouchViewQuery : function()
13701     {
13702         var qe = {
13703             query: '',
13704             forceAll: true,
13705             combo: this,
13706             cancel:false
13707         };
13708         
13709         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13710             return false;
13711         }
13712         
13713         if(!this.alwaysQuery || this.mode == 'local'){
13714             this.onTouchViewLoad();
13715             return;
13716         }
13717         
13718         this.store.load();
13719     },
13720     
13721     onTouchViewBeforeLoad : function(combo,opts)
13722     {
13723         return;
13724     },
13725
13726     // private
13727     onTouchViewLoad : function()
13728     {
13729         if(this.store.getCount() < 1){
13730             this.onTouchViewEmptyResults();
13731             return;
13732         }
13733         
13734         this.clearTouchView();
13735         
13736         var rawValue = this.getRawValue();
13737         
13738         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13739         
13740         this.tickItems = [];
13741         
13742         this.store.data.each(function(d, rowIndex){
13743             var row = this.touchViewListGroup.createChild(template);
13744             
13745             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13746                 var cfg = {
13747                     data : d.data,
13748                     html : d.data[this.displayField]
13749                 };
13750                 
13751                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13752                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13753                 }
13754             }
13755             
13756             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13757                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13758             }
13759             
13760             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13761                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13762                 this.tickItems.push(d.data);
13763             }
13764             
13765             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13766             
13767         }, this);
13768         
13769         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13770         
13771         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13772
13773         if(this.fieldLabel.length){
13774             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13775         }
13776
13777         var listHeight = this.touchViewListGroup.getHeight();
13778         
13779         var _this = this;
13780         
13781         if(firstChecked && listHeight > bodyHeight){
13782             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13783         }
13784         
13785     },
13786     
13787     onTouchViewLoadException : function()
13788     {
13789         this.hideTouchView();
13790     },
13791     
13792     onTouchViewEmptyResults : function()
13793     {
13794         this.clearTouchView();
13795         
13796         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13797         
13798         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13799         
13800     },
13801     
13802     clearTouchView : function()
13803     {
13804         this.touchViewListGroup.dom.innerHTML = '';
13805     },
13806     
13807     onTouchViewClick : function(e, el, o)
13808     {
13809         e.preventDefault();
13810         
13811         var row = o.row;
13812         var rowIndex = o.rowIndex;
13813         
13814         var r = this.store.getAt(rowIndex);
13815         
13816         if(!this.multiple){
13817             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13818                 c.dom.removeAttribute('checked');
13819             }, this);
13820             
13821             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13822         
13823             this.setFromData(r.data);
13824             
13825             var close = this.closeTriggerEl();
13826         
13827             if(close){
13828                 close.show();
13829             }
13830
13831             this.hideTouchView();
13832             
13833             this.fireEvent('select', this, r, rowIndex);
13834             
13835             return;
13836         }
13837         
13838         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13839             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13840             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13841             return;
13842         }
13843         
13844         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13845         this.addItem(r.data);
13846         this.tickItems.push(r.data);
13847         
13848     }
13849     
13850
13851     /** 
13852     * @cfg {Boolean} grow 
13853     * @hide 
13854     */
13855     /** 
13856     * @cfg {Number} growMin 
13857     * @hide 
13858     */
13859     /** 
13860     * @cfg {Number} growMax 
13861     * @hide 
13862     */
13863     /**
13864      * @hide
13865      * @method autoSize
13866      */
13867 });
13868
13869 Roo.apply(Roo.bootstrap.ComboBox,  {
13870     
13871     header : {
13872         tag: 'div',
13873         cls: 'modal-header',
13874         cn: [
13875             {
13876                 tag: 'h4',
13877                 cls: 'modal-title'
13878             }
13879         ]
13880     },
13881     
13882     body : {
13883         tag: 'div',
13884         cls: 'modal-body',
13885         cn: [
13886             {
13887                 tag: 'ul',
13888                 cls: 'list-group'
13889             }
13890         ]
13891     },
13892     
13893     listItemRadio : {
13894         tag: 'li',
13895         cls: 'list-group-item',
13896         cn: [
13897             {
13898                 tag: 'span',
13899                 cls: 'roo-combobox-list-group-item-value'
13900             },
13901             {
13902                 tag: 'div',
13903                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13904                 cn: [
13905                     {
13906                         tag: 'input',
13907                         type: 'radio'
13908                     },
13909                     {
13910                         tag: 'label'
13911                     }
13912                 ]
13913             }
13914         ]
13915     },
13916     
13917     listItemCheckbox : {
13918         tag: 'li',
13919         cls: 'list-group-item',
13920         cn: [
13921             {
13922                 tag: 'span',
13923                 cls: 'roo-combobox-list-group-item-value'
13924             },
13925             {
13926                 tag: 'div',
13927                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13928                 cn: [
13929                     {
13930                         tag: 'input',
13931                         type: 'checkbox'
13932                     },
13933                     {
13934                         tag: 'label'
13935                     }
13936                 ]
13937             }
13938         ]
13939     },
13940     
13941     emptyResult : {
13942         tag: 'div',
13943         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13944     },
13945     
13946     footer : {
13947         tag: 'div',
13948         cls: 'modal-footer',
13949         cn: [
13950             {
13951                 tag: 'div',
13952                 cls: 'row',
13953                 cn: [
13954                     {
13955                         tag: 'div',
13956                         cls: 'col-xs-6 text-left',
13957                         cn: {
13958                             tag: 'button',
13959                             cls: 'btn btn-danger roo-touch-view-cancel',
13960                             html: 'Cancel'
13961                         }
13962                     },
13963                     {
13964                         tag: 'div',
13965                         cls: 'col-xs-6 text-right',
13966                         cn: {
13967                             tag: 'button',
13968                             cls: 'btn btn-success roo-touch-view-ok',
13969                             html: 'OK'
13970                         }
13971                     }
13972                 ]
13973             }
13974         ]
13975         
13976     }
13977 });
13978
13979 Roo.apply(Roo.bootstrap.ComboBox,  {
13980     
13981     touchViewTemplate : {
13982         tag: 'div',
13983         cls: 'modal fade roo-combobox-touch-view',
13984         cn: [
13985             {
13986                 tag: 'div',
13987                 cls: 'modal-dialog',
13988                 cn: [
13989                     {
13990                         tag: 'div',
13991                         cls: 'modal-content',
13992                         cn: [
13993                             Roo.bootstrap.ComboBox.header,
13994                             Roo.bootstrap.ComboBox.body,
13995                             Roo.bootstrap.ComboBox.footer
13996                         ]
13997                     }
13998                 ]
13999             }
14000         ]
14001     }
14002 });/*
14003  * Based on:
14004  * Ext JS Library 1.1.1
14005  * Copyright(c) 2006-2007, Ext JS, LLC.
14006  *
14007  * Originally Released Under LGPL - original licence link has changed is not relivant.
14008  *
14009  * Fork - LGPL
14010  * <script type="text/javascript">
14011  */
14012
14013 /**
14014  * @class Roo.View
14015  * @extends Roo.util.Observable
14016  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14017  * This class also supports single and multi selection modes. <br>
14018  * Create a data model bound view:
14019  <pre><code>
14020  var store = new Roo.data.Store(...);
14021
14022  var view = new Roo.View({
14023     el : "my-element",
14024     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14025  
14026     singleSelect: true,
14027     selectedClass: "ydataview-selected",
14028     store: store
14029  });
14030
14031  // listen for node click?
14032  view.on("click", function(vw, index, node, e){
14033  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14034  });
14035
14036  // load XML data
14037  dataModel.load("foobar.xml");
14038  </code></pre>
14039  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14040  * <br><br>
14041  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14042  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14043  * 
14044  * Note: old style constructor is still suported (container, template, config)
14045  * 
14046  * @constructor
14047  * Create a new View
14048  * @param {Object} config The config object
14049  * 
14050  */
14051 Roo.View = function(config, depreciated_tpl, depreciated_config){
14052     
14053     this.parent = false;
14054     
14055     if (typeof(depreciated_tpl) == 'undefined') {
14056         // new way.. - universal constructor.
14057         Roo.apply(this, config);
14058         this.el  = Roo.get(this.el);
14059     } else {
14060         // old format..
14061         this.el  = Roo.get(config);
14062         this.tpl = depreciated_tpl;
14063         Roo.apply(this, depreciated_config);
14064     }
14065     this.wrapEl  = this.el.wrap().wrap();
14066     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14067     
14068     
14069     if(typeof(this.tpl) == "string"){
14070         this.tpl = new Roo.Template(this.tpl);
14071     } else {
14072         // support xtype ctors..
14073         this.tpl = new Roo.factory(this.tpl, Roo);
14074     }
14075     
14076     
14077     this.tpl.compile();
14078     
14079     /** @private */
14080     this.addEvents({
14081         /**
14082          * @event beforeclick
14083          * Fires before a click is processed. Returns false to cancel the default action.
14084          * @param {Roo.View} this
14085          * @param {Number} index The index of the target node
14086          * @param {HTMLElement} node The target node
14087          * @param {Roo.EventObject} e The raw event object
14088          */
14089             "beforeclick" : true,
14090         /**
14091          * @event click
14092          * Fires when a template node is clicked.
14093          * @param {Roo.View} this
14094          * @param {Number} index The index of the target node
14095          * @param {HTMLElement} node The target node
14096          * @param {Roo.EventObject} e The raw event object
14097          */
14098             "click" : true,
14099         /**
14100          * @event dblclick
14101          * Fires when a template node is double clicked.
14102          * @param {Roo.View} this
14103          * @param {Number} index The index of the target node
14104          * @param {HTMLElement} node The target node
14105          * @param {Roo.EventObject} e The raw event object
14106          */
14107             "dblclick" : true,
14108         /**
14109          * @event contextmenu
14110          * Fires when a template node is right clicked.
14111          * @param {Roo.View} this
14112          * @param {Number} index The index of the target node
14113          * @param {HTMLElement} node The target node
14114          * @param {Roo.EventObject} e The raw event object
14115          */
14116             "contextmenu" : true,
14117         /**
14118          * @event selectionchange
14119          * Fires when the selected nodes change.
14120          * @param {Roo.View} this
14121          * @param {Array} selections Array of the selected nodes
14122          */
14123             "selectionchange" : true,
14124     
14125         /**
14126          * @event beforeselect
14127          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14128          * @param {Roo.View} this
14129          * @param {HTMLElement} node The node to be selected
14130          * @param {Array} selections Array of currently selected nodes
14131          */
14132             "beforeselect" : true,
14133         /**
14134          * @event preparedata
14135          * Fires on every row to render, to allow you to change the data.
14136          * @param {Roo.View} this
14137          * @param {Object} data to be rendered (change this)
14138          */
14139           "preparedata" : true
14140           
14141           
14142         });
14143
14144
14145
14146     this.el.on({
14147         "click": this.onClick,
14148         "dblclick": this.onDblClick,
14149         "contextmenu": this.onContextMenu,
14150         scope:this
14151     });
14152
14153     this.selections = [];
14154     this.nodes = [];
14155     this.cmp = new Roo.CompositeElementLite([]);
14156     if(this.store){
14157         this.store = Roo.factory(this.store, Roo.data);
14158         this.setStore(this.store, true);
14159     }
14160     
14161     if ( this.footer && this.footer.xtype) {
14162            
14163          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14164         
14165         this.footer.dataSource = this.store;
14166         this.footer.container = fctr;
14167         this.footer = Roo.factory(this.footer, Roo);
14168         fctr.insertFirst(this.el);
14169         
14170         // this is a bit insane - as the paging toolbar seems to detach the el..
14171 //        dom.parentNode.parentNode.parentNode
14172          // they get detached?
14173     }
14174     
14175     
14176     Roo.View.superclass.constructor.call(this);
14177     
14178     
14179 };
14180
14181 Roo.extend(Roo.View, Roo.util.Observable, {
14182     
14183      /**
14184      * @cfg {Roo.data.Store} store Data store to load data from.
14185      */
14186     store : false,
14187     
14188     /**
14189      * @cfg {String|Roo.Element} el The container element.
14190      */
14191     el : '',
14192     
14193     /**
14194      * @cfg {String|Roo.Template} tpl The template used by this View 
14195      */
14196     tpl : false,
14197     /**
14198      * @cfg {String} dataName the named area of the template to use as the data area
14199      *                          Works with domtemplates roo-name="name"
14200      */
14201     dataName: false,
14202     /**
14203      * @cfg {String} selectedClass The css class to add to selected nodes
14204      */
14205     selectedClass : "x-view-selected",
14206      /**
14207      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14208      */
14209     emptyText : "",
14210     
14211     /**
14212      * @cfg {String} text to display on mask (default Loading)
14213      */
14214     mask : false,
14215     /**
14216      * @cfg {Boolean} multiSelect Allow multiple selection
14217      */
14218     multiSelect : false,
14219     /**
14220      * @cfg {Boolean} singleSelect Allow single selection
14221      */
14222     singleSelect:  false,
14223     
14224     /**
14225      * @cfg {Boolean} toggleSelect - selecting 
14226      */
14227     toggleSelect : false,
14228     
14229     /**
14230      * @cfg {Boolean} tickable - selecting 
14231      */
14232     tickable : false,
14233     
14234     /**
14235      * Returns the element this view is bound to.
14236      * @return {Roo.Element}
14237      */
14238     getEl : function(){
14239         return this.wrapEl;
14240     },
14241     
14242     
14243
14244     /**
14245      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14246      */
14247     refresh : function(){
14248         //Roo.log('refresh');
14249         var t = this.tpl;
14250         
14251         // if we are using something like 'domtemplate', then
14252         // the what gets used is:
14253         // t.applySubtemplate(NAME, data, wrapping data..)
14254         // the outer template then get' applied with
14255         //     the store 'extra data'
14256         // and the body get's added to the
14257         //      roo-name="data" node?
14258         //      <span class='roo-tpl-{name}'></span> ?????
14259         
14260         
14261         
14262         this.clearSelections();
14263         this.el.update("");
14264         var html = [];
14265         var records = this.store.getRange();
14266         if(records.length < 1) {
14267             
14268             // is this valid??  = should it render a template??
14269             
14270             this.el.update(this.emptyText);
14271             return;
14272         }
14273         var el = this.el;
14274         if (this.dataName) {
14275             this.el.update(t.apply(this.store.meta)); //????
14276             el = this.el.child('.roo-tpl-' + this.dataName);
14277         }
14278         
14279         for(var i = 0, len = records.length; i < len; i++){
14280             var data = this.prepareData(records[i].data, i, records[i]);
14281             this.fireEvent("preparedata", this, data, i, records[i]);
14282             
14283             var d = Roo.apply({}, data);
14284             
14285             if(this.tickable){
14286                 Roo.apply(d, {'roo-id' : Roo.id()});
14287                 
14288                 var _this = this;
14289             
14290                 Roo.each(this.parent.item, function(item){
14291                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14292                         return;
14293                     }
14294                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14295                 });
14296             }
14297             
14298             html[html.length] = Roo.util.Format.trim(
14299                 this.dataName ?
14300                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14301                     t.apply(d)
14302             );
14303         }
14304         
14305         
14306         
14307         el.update(html.join(""));
14308         this.nodes = el.dom.childNodes;
14309         this.updateIndexes(0);
14310     },
14311     
14312
14313     /**
14314      * Function to override to reformat the data that is sent to
14315      * the template for each node.
14316      * DEPRICATED - use the preparedata event handler.
14317      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14318      * a JSON object for an UpdateManager bound view).
14319      */
14320     prepareData : function(data, index, record)
14321     {
14322         this.fireEvent("preparedata", this, data, index, record);
14323         return data;
14324     },
14325
14326     onUpdate : function(ds, record){
14327         // Roo.log('on update');   
14328         this.clearSelections();
14329         var index = this.store.indexOf(record);
14330         var n = this.nodes[index];
14331         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14332         n.parentNode.removeChild(n);
14333         this.updateIndexes(index, index);
14334     },
14335
14336     
14337     
14338 // --------- FIXME     
14339     onAdd : function(ds, records, index)
14340     {
14341         //Roo.log(['on Add', ds, records, index] );        
14342         this.clearSelections();
14343         if(this.nodes.length == 0){
14344             this.refresh();
14345             return;
14346         }
14347         var n = this.nodes[index];
14348         for(var i = 0, len = records.length; i < len; i++){
14349             var d = this.prepareData(records[i].data, i, records[i]);
14350             if(n){
14351                 this.tpl.insertBefore(n, d);
14352             }else{
14353                 
14354                 this.tpl.append(this.el, d);
14355             }
14356         }
14357         this.updateIndexes(index);
14358     },
14359
14360     onRemove : function(ds, record, index){
14361        // Roo.log('onRemove');
14362         this.clearSelections();
14363         var el = this.dataName  ?
14364             this.el.child('.roo-tpl-' + this.dataName) :
14365             this.el; 
14366         
14367         el.dom.removeChild(this.nodes[index]);
14368         this.updateIndexes(index);
14369     },
14370
14371     /**
14372      * Refresh an individual node.
14373      * @param {Number} index
14374      */
14375     refreshNode : function(index){
14376         this.onUpdate(this.store, this.store.getAt(index));
14377     },
14378
14379     updateIndexes : function(startIndex, endIndex){
14380         var ns = this.nodes;
14381         startIndex = startIndex || 0;
14382         endIndex = endIndex || ns.length - 1;
14383         for(var i = startIndex; i <= endIndex; i++){
14384             ns[i].nodeIndex = i;
14385         }
14386     },
14387
14388     /**
14389      * Changes the data store this view uses and refresh the view.
14390      * @param {Store} store
14391      */
14392     setStore : function(store, initial){
14393         if(!initial && this.store){
14394             this.store.un("datachanged", this.refresh);
14395             this.store.un("add", this.onAdd);
14396             this.store.un("remove", this.onRemove);
14397             this.store.un("update", this.onUpdate);
14398             this.store.un("clear", this.refresh);
14399             this.store.un("beforeload", this.onBeforeLoad);
14400             this.store.un("load", this.onLoad);
14401             this.store.un("loadexception", this.onLoad);
14402         }
14403         if(store){
14404           
14405             store.on("datachanged", this.refresh, this);
14406             store.on("add", this.onAdd, this);
14407             store.on("remove", this.onRemove, this);
14408             store.on("update", this.onUpdate, this);
14409             store.on("clear", this.refresh, this);
14410             store.on("beforeload", this.onBeforeLoad, this);
14411             store.on("load", this.onLoad, this);
14412             store.on("loadexception", this.onLoad, this);
14413         }
14414         
14415         if(store){
14416             this.refresh();
14417         }
14418     },
14419     /**
14420      * onbeforeLoad - masks the loading area.
14421      *
14422      */
14423     onBeforeLoad : function(store,opts)
14424     {
14425          //Roo.log('onBeforeLoad');   
14426         if (!opts.add) {
14427             this.el.update("");
14428         }
14429         this.el.mask(this.mask ? this.mask : "Loading" ); 
14430     },
14431     onLoad : function ()
14432     {
14433         this.el.unmask();
14434     },
14435     
14436
14437     /**
14438      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14439      * @param {HTMLElement} node
14440      * @return {HTMLElement} The template node
14441      */
14442     findItemFromChild : function(node){
14443         var el = this.dataName  ?
14444             this.el.child('.roo-tpl-' + this.dataName,true) :
14445             this.el.dom; 
14446         
14447         if(!node || node.parentNode == el){
14448                     return node;
14449             }
14450             var p = node.parentNode;
14451             while(p && p != el){
14452             if(p.parentNode == el){
14453                 return p;
14454             }
14455             p = p.parentNode;
14456         }
14457             return null;
14458     },
14459
14460     /** @ignore */
14461     onClick : function(e){
14462         var item = this.findItemFromChild(e.getTarget());
14463         if(item){
14464             var index = this.indexOf(item);
14465             if(this.onItemClick(item, index, e) !== false){
14466                 this.fireEvent("click", this, index, item, e);
14467             }
14468         }else{
14469             this.clearSelections();
14470         }
14471     },
14472
14473     /** @ignore */
14474     onContextMenu : function(e){
14475         var item = this.findItemFromChild(e.getTarget());
14476         if(item){
14477             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14478         }
14479     },
14480
14481     /** @ignore */
14482     onDblClick : function(e){
14483         var item = this.findItemFromChild(e.getTarget());
14484         if(item){
14485             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14486         }
14487     },
14488
14489     onItemClick : function(item, index, e)
14490     {
14491         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14492             return false;
14493         }
14494         if (this.toggleSelect) {
14495             var m = this.isSelected(item) ? 'unselect' : 'select';
14496             //Roo.log(m);
14497             var _t = this;
14498             _t[m](item, true, false);
14499             return true;
14500         }
14501         if(this.multiSelect || this.singleSelect){
14502             if(this.multiSelect && e.shiftKey && this.lastSelection){
14503                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14504             }else{
14505                 this.select(item, this.multiSelect && e.ctrlKey);
14506                 this.lastSelection = item;
14507             }
14508             
14509             if(!this.tickable){
14510                 e.preventDefault();
14511             }
14512             
14513         }
14514         return true;
14515     },
14516
14517     /**
14518      * Get the number of selected nodes.
14519      * @return {Number}
14520      */
14521     getSelectionCount : function(){
14522         return this.selections.length;
14523     },
14524
14525     /**
14526      * Get the currently selected nodes.
14527      * @return {Array} An array of HTMLElements
14528      */
14529     getSelectedNodes : function(){
14530         return this.selections;
14531     },
14532
14533     /**
14534      * Get the indexes of the selected nodes.
14535      * @return {Array}
14536      */
14537     getSelectedIndexes : function(){
14538         var indexes = [], s = this.selections;
14539         for(var i = 0, len = s.length; i < len; i++){
14540             indexes.push(s[i].nodeIndex);
14541         }
14542         return indexes;
14543     },
14544
14545     /**
14546      * Clear all selections
14547      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14548      */
14549     clearSelections : function(suppressEvent){
14550         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14551             this.cmp.elements = this.selections;
14552             this.cmp.removeClass(this.selectedClass);
14553             this.selections = [];
14554             if(!suppressEvent){
14555                 this.fireEvent("selectionchange", this, this.selections);
14556             }
14557         }
14558     },
14559
14560     /**
14561      * Returns true if the passed node is selected
14562      * @param {HTMLElement/Number} node The node or node index
14563      * @return {Boolean}
14564      */
14565     isSelected : function(node){
14566         var s = this.selections;
14567         if(s.length < 1){
14568             return false;
14569         }
14570         node = this.getNode(node);
14571         return s.indexOf(node) !== -1;
14572     },
14573
14574     /**
14575      * Selects nodes.
14576      * @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
14577      * @param {Boolean} keepExisting (optional) true to keep existing selections
14578      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14579      */
14580     select : function(nodeInfo, keepExisting, suppressEvent){
14581         if(nodeInfo instanceof Array){
14582             if(!keepExisting){
14583                 this.clearSelections(true);
14584             }
14585             for(var i = 0, len = nodeInfo.length; i < len; i++){
14586                 this.select(nodeInfo[i], true, true);
14587             }
14588             return;
14589         } 
14590         var node = this.getNode(nodeInfo);
14591         if(!node || this.isSelected(node)){
14592             return; // already selected.
14593         }
14594         if(!keepExisting){
14595             this.clearSelections(true);
14596         }
14597         
14598         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14599             Roo.fly(node).addClass(this.selectedClass);
14600             this.selections.push(node);
14601             if(!suppressEvent){
14602                 this.fireEvent("selectionchange", this, this.selections);
14603             }
14604         }
14605         
14606         
14607     },
14608       /**
14609      * Unselects nodes.
14610      * @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
14611      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14612      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14613      */
14614     unselect : function(nodeInfo, keepExisting, suppressEvent)
14615     {
14616         if(nodeInfo instanceof Array){
14617             Roo.each(this.selections, function(s) {
14618                 this.unselect(s, nodeInfo);
14619             }, this);
14620             return;
14621         }
14622         var node = this.getNode(nodeInfo);
14623         if(!node || !this.isSelected(node)){
14624             //Roo.log("not selected");
14625             return; // not selected.
14626         }
14627         // fireevent???
14628         var ns = [];
14629         Roo.each(this.selections, function(s) {
14630             if (s == node ) {
14631                 Roo.fly(node).removeClass(this.selectedClass);
14632
14633                 return;
14634             }
14635             ns.push(s);
14636         },this);
14637         
14638         this.selections= ns;
14639         this.fireEvent("selectionchange", this, this.selections);
14640     },
14641
14642     /**
14643      * Gets a template node.
14644      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14645      * @return {HTMLElement} The node or null if it wasn't found
14646      */
14647     getNode : function(nodeInfo){
14648         if(typeof nodeInfo == "string"){
14649             return document.getElementById(nodeInfo);
14650         }else if(typeof nodeInfo == "number"){
14651             return this.nodes[nodeInfo];
14652         }
14653         return nodeInfo;
14654     },
14655
14656     /**
14657      * Gets a range template nodes.
14658      * @param {Number} startIndex
14659      * @param {Number} endIndex
14660      * @return {Array} An array of nodes
14661      */
14662     getNodes : function(start, end){
14663         var ns = this.nodes;
14664         start = start || 0;
14665         end = typeof end == "undefined" ? ns.length - 1 : end;
14666         var nodes = [];
14667         if(start <= end){
14668             for(var i = start; i <= end; i++){
14669                 nodes.push(ns[i]);
14670             }
14671         } else{
14672             for(var i = start; i >= end; i--){
14673                 nodes.push(ns[i]);
14674             }
14675         }
14676         return nodes;
14677     },
14678
14679     /**
14680      * Finds the index of the passed node
14681      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14682      * @return {Number} The index of the node or -1
14683      */
14684     indexOf : function(node){
14685         node = this.getNode(node);
14686         if(typeof node.nodeIndex == "number"){
14687             return node.nodeIndex;
14688         }
14689         var ns = this.nodes;
14690         for(var i = 0, len = ns.length; i < len; i++){
14691             if(ns[i] == node){
14692                 return i;
14693             }
14694         }
14695         return -1;
14696     }
14697 });
14698 /*
14699  * - LGPL
14700  *
14701  * based on jquery fullcalendar
14702  * 
14703  */
14704
14705 Roo.bootstrap = Roo.bootstrap || {};
14706 /**
14707  * @class Roo.bootstrap.Calendar
14708  * @extends Roo.bootstrap.Component
14709  * Bootstrap Calendar class
14710  * @cfg {Boolean} loadMask (true|false) default false
14711  * @cfg {Object} header generate the user specific header of the calendar, default false
14712
14713  * @constructor
14714  * Create a new Container
14715  * @param {Object} config The config object
14716  */
14717
14718
14719
14720 Roo.bootstrap.Calendar = function(config){
14721     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14722      this.addEvents({
14723         /**
14724              * @event select
14725              * Fires when a date is selected
14726              * @param {DatePicker} this
14727              * @param {Date} date The selected date
14728              */
14729         'select': true,
14730         /**
14731              * @event monthchange
14732              * Fires when the displayed month changes 
14733              * @param {DatePicker} this
14734              * @param {Date} date The selected month
14735              */
14736         'monthchange': true,
14737         /**
14738              * @event evententer
14739              * Fires when mouse over an event
14740              * @param {Calendar} this
14741              * @param {event} Event
14742              */
14743         'evententer': true,
14744         /**
14745              * @event eventleave
14746              * Fires when the mouse leaves an
14747              * @param {Calendar} this
14748              * @param {event}
14749              */
14750         'eventleave': true,
14751         /**
14752              * @event eventclick
14753              * Fires when the mouse click an
14754              * @param {Calendar} this
14755              * @param {event}
14756              */
14757         'eventclick': true
14758         
14759     });
14760
14761 };
14762
14763 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14764     
14765      /**
14766      * @cfg {Number} startDay
14767      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14768      */
14769     startDay : 0,
14770     
14771     loadMask : false,
14772     
14773     header : false,
14774       
14775     getAutoCreate : function(){
14776         
14777         
14778         var fc_button = function(name, corner, style, content ) {
14779             return Roo.apply({},{
14780                 tag : 'span',
14781                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14782                          (corner.length ?
14783                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14784                             ''
14785                         ),
14786                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14787                 unselectable: 'on'
14788             });
14789         };
14790         
14791         var header = {};
14792         
14793         if(!this.header){
14794             header = {
14795                 tag : 'table',
14796                 cls : 'fc-header',
14797                 style : 'width:100%',
14798                 cn : [
14799                     {
14800                         tag: 'tr',
14801                         cn : [
14802                             {
14803                                 tag : 'td',
14804                                 cls : 'fc-header-left',
14805                                 cn : [
14806                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14807                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14808                                     { tag: 'span', cls: 'fc-header-space' },
14809                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14810
14811
14812                                 ]
14813                             },
14814
14815                             {
14816                                 tag : 'td',
14817                                 cls : 'fc-header-center',
14818                                 cn : [
14819                                     {
14820                                         tag: 'span',
14821                                         cls: 'fc-header-title',
14822                                         cn : {
14823                                             tag: 'H2',
14824                                             html : 'month / year'
14825                                         }
14826                                     }
14827
14828                                 ]
14829                             },
14830                             {
14831                                 tag : 'td',
14832                                 cls : 'fc-header-right',
14833                                 cn : [
14834                               /*      fc_button('month', 'left', '', 'month' ),
14835                                     fc_button('week', '', '', 'week' ),
14836                                     fc_button('day', 'right', '', 'day' )
14837                                 */    
14838
14839                                 ]
14840                             }
14841
14842                         ]
14843                     }
14844                 ]
14845             };
14846         }
14847         
14848         header = this.header;
14849         
14850        
14851         var cal_heads = function() {
14852             var ret = [];
14853             // fixme - handle this.
14854             
14855             for (var i =0; i < Date.dayNames.length; i++) {
14856                 var d = Date.dayNames[i];
14857                 ret.push({
14858                     tag: 'th',
14859                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14860                     html : d.substring(0,3)
14861                 });
14862                 
14863             }
14864             ret[0].cls += ' fc-first';
14865             ret[6].cls += ' fc-last';
14866             return ret;
14867         };
14868         var cal_cell = function(n) {
14869             return  {
14870                 tag: 'td',
14871                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14872                 cn : [
14873                     {
14874                         cn : [
14875                             {
14876                                 cls: 'fc-day-number',
14877                                 html: 'D'
14878                             },
14879                             {
14880                                 cls: 'fc-day-content',
14881                              
14882                                 cn : [
14883                                      {
14884                                         style: 'position: relative;' // height: 17px;
14885                                     }
14886                                 ]
14887                             }
14888                             
14889                             
14890                         ]
14891                     }
14892                 ]
14893                 
14894             }
14895         };
14896         var cal_rows = function() {
14897             
14898             var ret = [];
14899             for (var r = 0; r < 6; r++) {
14900                 var row= {
14901                     tag : 'tr',
14902                     cls : 'fc-week',
14903                     cn : []
14904                 };
14905                 
14906                 for (var i =0; i < Date.dayNames.length; i++) {
14907                     var d = Date.dayNames[i];
14908                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14909
14910                 }
14911                 row.cn[0].cls+=' fc-first';
14912                 row.cn[0].cn[0].style = 'min-height:90px';
14913                 row.cn[6].cls+=' fc-last';
14914                 ret.push(row);
14915                 
14916             }
14917             ret[0].cls += ' fc-first';
14918             ret[4].cls += ' fc-prev-last';
14919             ret[5].cls += ' fc-last';
14920             return ret;
14921             
14922         };
14923         
14924         var cal_table = {
14925             tag: 'table',
14926             cls: 'fc-border-separate',
14927             style : 'width:100%',
14928             cellspacing  : 0,
14929             cn : [
14930                 { 
14931                     tag: 'thead',
14932                     cn : [
14933                         { 
14934                             tag: 'tr',
14935                             cls : 'fc-first fc-last',
14936                             cn : cal_heads()
14937                         }
14938                     ]
14939                 },
14940                 { 
14941                     tag: 'tbody',
14942                     cn : cal_rows()
14943                 }
14944                   
14945             ]
14946         };
14947          
14948          var cfg = {
14949             cls : 'fc fc-ltr',
14950             cn : [
14951                 header,
14952                 {
14953                     cls : 'fc-content',
14954                     style : "position: relative;",
14955                     cn : [
14956                         {
14957                             cls : 'fc-view fc-view-month fc-grid',
14958                             style : 'position: relative',
14959                             unselectable : 'on',
14960                             cn : [
14961                                 {
14962                                     cls : 'fc-event-container',
14963                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14964                                 },
14965                                 cal_table
14966                             ]
14967                         }
14968                     ]
14969     
14970                 }
14971            ] 
14972             
14973         };
14974         
14975          
14976         
14977         return cfg;
14978     },
14979     
14980     
14981     initEvents : function()
14982     {
14983         if(!this.store){
14984             throw "can not find store for calendar";
14985         }
14986         
14987         var mark = {
14988             tag: "div",
14989             cls:"x-dlg-mask",
14990             style: "text-align:center",
14991             cn: [
14992                 {
14993                     tag: "div",
14994                     style: "background-color:white;width:50%;margin:250 auto",
14995                     cn: [
14996                         {
14997                             tag: "img",
14998                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14999                         },
15000                         {
15001                             tag: "span",
15002                             html: "Loading"
15003                         }
15004                         
15005                     ]
15006                 }
15007             ]
15008         };
15009         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15010         
15011         var size = this.el.select('.fc-content', true).first().getSize();
15012         this.maskEl.setSize(size.width, size.height);
15013         this.maskEl.enableDisplayMode("block");
15014         if(!this.loadMask){
15015             this.maskEl.hide();
15016         }
15017         
15018         this.store = Roo.factory(this.store, Roo.data);
15019         this.store.on('load', this.onLoad, this);
15020         this.store.on('beforeload', this.onBeforeLoad, this);
15021         
15022         this.resize();
15023         
15024         this.cells = this.el.select('.fc-day',true);
15025         //Roo.log(this.cells);
15026         this.textNodes = this.el.query('.fc-day-number');
15027         this.cells.addClassOnOver('fc-state-hover');
15028         
15029         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15030         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15031         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15032         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15033         
15034         this.on('monthchange', this.onMonthChange, this);
15035         
15036         this.update(new Date().clearTime());
15037     },
15038     
15039     resize : function() {
15040         var sz  = this.el.getSize();
15041         
15042         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15043         this.el.select('.fc-day-content div',true).setHeight(34);
15044     },
15045     
15046     
15047     // private
15048     showPrevMonth : function(e){
15049         this.update(this.activeDate.add("mo", -1));
15050     },
15051     showToday : function(e){
15052         this.update(new Date().clearTime());
15053     },
15054     // private
15055     showNextMonth : function(e){
15056         this.update(this.activeDate.add("mo", 1));
15057     },
15058
15059     // private
15060     showPrevYear : function(){
15061         this.update(this.activeDate.add("y", -1));
15062     },
15063
15064     // private
15065     showNextYear : function(){
15066         this.update(this.activeDate.add("y", 1));
15067     },
15068
15069     
15070    // private
15071     update : function(date)
15072     {
15073         var vd = this.activeDate;
15074         this.activeDate = date;
15075 //        if(vd && this.el){
15076 //            var t = date.getTime();
15077 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15078 //                Roo.log('using add remove');
15079 //                
15080 //                this.fireEvent('monthchange', this, date);
15081 //                
15082 //                this.cells.removeClass("fc-state-highlight");
15083 //                this.cells.each(function(c){
15084 //                   if(c.dateValue == t){
15085 //                       c.addClass("fc-state-highlight");
15086 //                       setTimeout(function(){
15087 //                            try{c.dom.firstChild.focus();}catch(e){}
15088 //                       }, 50);
15089 //                       return false;
15090 //                   }
15091 //                   return true;
15092 //                });
15093 //                return;
15094 //            }
15095 //        }
15096         
15097         var days = date.getDaysInMonth();
15098         
15099         var firstOfMonth = date.getFirstDateOfMonth();
15100         var startingPos = firstOfMonth.getDay()-this.startDay;
15101         
15102         if(startingPos < this.startDay){
15103             startingPos += 7;
15104         }
15105         
15106         var pm = date.add(Date.MONTH, -1);
15107         var prevStart = pm.getDaysInMonth()-startingPos;
15108 //        
15109         this.cells = this.el.select('.fc-day',true);
15110         this.textNodes = this.el.query('.fc-day-number');
15111         this.cells.addClassOnOver('fc-state-hover');
15112         
15113         var cells = this.cells.elements;
15114         var textEls = this.textNodes;
15115         
15116         Roo.each(cells, function(cell){
15117             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15118         });
15119         
15120         days += startingPos;
15121
15122         // convert everything to numbers so it's fast
15123         var day = 86400000;
15124         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15125         //Roo.log(d);
15126         //Roo.log(pm);
15127         //Roo.log(prevStart);
15128         
15129         var today = new Date().clearTime().getTime();
15130         var sel = date.clearTime().getTime();
15131         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15132         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15133         var ddMatch = this.disabledDatesRE;
15134         var ddText = this.disabledDatesText;
15135         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15136         var ddaysText = this.disabledDaysText;
15137         var format = this.format;
15138         
15139         var setCellClass = function(cal, cell){
15140             cell.row = 0;
15141             cell.events = [];
15142             cell.more = [];
15143             //Roo.log('set Cell Class');
15144             cell.title = "";
15145             var t = d.getTime();
15146             
15147             //Roo.log(d);
15148             
15149             cell.dateValue = t;
15150             if(t == today){
15151                 cell.className += " fc-today";
15152                 cell.className += " fc-state-highlight";
15153                 cell.title = cal.todayText;
15154             }
15155             if(t == sel){
15156                 // disable highlight in other month..
15157                 //cell.className += " fc-state-highlight";
15158                 
15159             }
15160             // disabling
15161             if(t < min) {
15162                 cell.className = " fc-state-disabled";
15163                 cell.title = cal.minText;
15164                 return;
15165             }
15166             if(t > max) {
15167                 cell.className = " fc-state-disabled";
15168                 cell.title = cal.maxText;
15169                 return;
15170             }
15171             if(ddays){
15172                 if(ddays.indexOf(d.getDay()) != -1){
15173                     cell.title = ddaysText;
15174                     cell.className = " fc-state-disabled";
15175                 }
15176             }
15177             if(ddMatch && format){
15178                 var fvalue = d.dateFormat(format);
15179                 if(ddMatch.test(fvalue)){
15180                     cell.title = ddText.replace("%0", fvalue);
15181                     cell.className = " fc-state-disabled";
15182                 }
15183             }
15184             
15185             if (!cell.initialClassName) {
15186                 cell.initialClassName = cell.dom.className;
15187             }
15188             
15189             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15190         };
15191
15192         var i = 0;
15193         
15194         for(; i < startingPos; i++) {
15195             textEls[i].innerHTML = (++prevStart);
15196             d.setDate(d.getDate()+1);
15197             
15198             cells[i].className = "fc-past fc-other-month";
15199             setCellClass(this, cells[i]);
15200         }
15201         
15202         var intDay = 0;
15203         
15204         for(; i < days; i++){
15205             intDay = i - startingPos + 1;
15206             textEls[i].innerHTML = (intDay);
15207             d.setDate(d.getDate()+1);
15208             
15209             cells[i].className = ''; // "x-date-active";
15210             setCellClass(this, cells[i]);
15211         }
15212         var extraDays = 0;
15213         
15214         for(; i < 42; i++) {
15215             textEls[i].innerHTML = (++extraDays);
15216             d.setDate(d.getDate()+1);
15217             
15218             cells[i].className = "fc-future fc-other-month";
15219             setCellClass(this, cells[i]);
15220         }
15221         
15222         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15223         
15224         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15225         
15226         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15227         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15228         
15229         if(totalRows != 6){
15230             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15231             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15232         }
15233         
15234         this.fireEvent('monthchange', this, date);
15235         
15236         
15237         /*
15238         if(!this.internalRender){
15239             var main = this.el.dom.firstChild;
15240             var w = main.offsetWidth;
15241             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15242             Roo.fly(main).setWidth(w);
15243             this.internalRender = true;
15244             // opera does not respect the auto grow header center column
15245             // then, after it gets a width opera refuses to recalculate
15246             // without a second pass
15247             if(Roo.isOpera && !this.secondPass){
15248                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15249                 this.secondPass = true;
15250                 this.update.defer(10, this, [date]);
15251             }
15252         }
15253         */
15254         
15255     },
15256     
15257     findCell : function(dt) {
15258         dt = dt.clearTime().getTime();
15259         var ret = false;
15260         this.cells.each(function(c){
15261             //Roo.log("check " +c.dateValue + '?=' + dt);
15262             if(c.dateValue == dt){
15263                 ret = c;
15264                 return false;
15265             }
15266             return true;
15267         });
15268         
15269         return ret;
15270     },
15271     
15272     findCells : function(ev) {
15273         var s = ev.start.clone().clearTime().getTime();
15274        // Roo.log(s);
15275         var e= ev.end.clone().clearTime().getTime();
15276        // Roo.log(e);
15277         var ret = [];
15278         this.cells.each(function(c){
15279              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15280             
15281             if(c.dateValue > e){
15282                 return ;
15283             }
15284             if(c.dateValue < s){
15285                 return ;
15286             }
15287             ret.push(c);
15288         });
15289         
15290         return ret;    
15291     },
15292     
15293 //    findBestRow: function(cells)
15294 //    {
15295 //        var ret = 0;
15296 //        
15297 //        for (var i =0 ; i < cells.length;i++) {
15298 //            ret  = Math.max(cells[i].rows || 0,ret);
15299 //        }
15300 //        return ret;
15301 //        
15302 //    },
15303     
15304     
15305     addItem : function(ev)
15306     {
15307         // look for vertical location slot in
15308         var cells = this.findCells(ev);
15309         
15310 //        ev.row = this.findBestRow(cells);
15311         
15312         // work out the location.
15313         
15314         var crow = false;
15315         var rows = [];
15316         for(var i =0; i < cells.length; i++) {
15317             
15318             cells[i].row = cells[0].row;
15319             
15320             if(i == 0){
15321                 cells[i].row = cells[i].row + 1;
15322             }
15323             
15324             if (!crow) {
15325                 crow = {
15326                     start : cells[i],
15327                     end :  cells[i]
15328                 };
15329                 continue;
15330             }
15331             if (crow.start.getY() == cells[i].getY()) {
15332                 // on same row.
15333                 crow.end = cells[i];
15334                 continue;
15335             }
15336             // different row.
15337             rows.push(crow);
15338             crow = {
15339                 start: cells[i],
15340                 end : cells[i]
15341             };
15342             
15343         }
15344         
15345         rows.push(crow);
15346         ev.els = [];
15347         ev.rows = rows;
15348         ev.cells = cells;
15349         
15350         cells[0].events.push(ev);
15351         
15352         this.calevents.push(ev);
15353     },
15354     
15355     clearEvents: function() {
15356         
15357         if(!this.calevents){
15358             return;
15359         }
15360         
15361         Roo.each(this.cells.elements, function(c){
15362             c.row = 0;
15363             c.events = [];
15364             c.more = [];
15365         });
15366         
15367         Roo.each(this.calevents, function(e) {
15368             Roo.each(e.els, function(el) {
15369                 el.un('mouseenter' ,this.onEventEnter, this);
15370                 el.un('mouseleave' ,this.onEventLeave, this);
15371                 el.remove();
15372             },this);
15373         },this);
15374         
15375         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15376             e.remove();
15377         });
15378         
15379     },
15380     
15381     renderEvents: function()
15382     {   
15383         var _this = this;
15384         
15385         this.cells.each(function(c) {
15386             
15387             if(c.row < 5){
15388                 return;
15389             }
15390             
15391             var ev = c.events;
15392             
15393             var r = 4;
15394             if(c.row != c.events.length){
15395                 r = 4 - (4 - (c.row - c.events.length));
15396             }
15397             
15398             c.events = ev.slice(0, r);
15399             c.more = ev.slice(r);
15400             
15401             if(c.more.length && c.more.length == 1){
15402                 c.events.push(c.more.pop());
15403             }
15404             
15405             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15406             
15407         });
15408             
15409         this.cells.each(function(c) {
15410             
15411             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15412             
15413             
15414             for (var e = 0; e < c.events.length; e++){
15415                 var ev = c.events[e];
15416                 var rows = ev.rows;
15417                 
15418                 for(var i = 0; i < rows.length; i++) {
15419                 
15420                     // how many rows should it span..
15421
15422                     var  cfg = {
15423                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15424                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15425
15426                         unselectable : "on",
15427                         cn : [
15428                             {
15429                                 cls: 'fc-event-inner',
15430                                 cn : [
15431     //                                {
15432     //                                  tag:'span',
15433     //                                  cls: 'fc-event-time',
15434     //                                  html : cells.length > 1 ? '' : ev.time
15435     //                                },
15436                                     {
15437                                       tag:'span',
15438                                       cls: 'fc-event-title',
15439                                       html : String.format('{0}', ev.title)
15440                                     }
15441
15442
15443                                 ]
15444                             },
15445                             {
15446                                 cls: 'ui-resizable-handle ui-resizable-e',
15447                                 html : '&nbsp;&nbsp;&nbsp'
15448                             }
15449
15450                         ]
15451                     };
15452
15453                     if (i == 0) {
15454                         cfg.cls += ' fc-event-start';
15455                     }
15456                     if ((i+1) == rows.length) {
15457                         cfg.cls += ' fc-event-end';
15458                     }
15459
15460                     var ctr = _this.el.select('.fc-event-container',true).first();
15461                     var cg = ctr.createChild(cfg);
15462
15463                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15464                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15465
15466                     var r = (c.more.length) ? 1 : 0;
15467                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15468                     cg.setWidth(ebox.right - sbox.x -2);
15469
15470                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15471                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15472                     cg.on('click', _this.onEventClick, _this, ev);
15473
15474                     ev.els.push(cg);
15475                     
15476                 }
15477                 
15478             }
15479             
15480             
15481             if(c.more.length){
15482                 var  cfg = {
15483                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15484                     style : 'position: absolute',
15485                     unselectable : "on",
15486                     cn : [
15487                         {
15488                             cls: 'fc-event-inner',
15489                             cn : [
15490                                 {
15491                                   tag:'span',
15492                                   cls: 'fc-event-title',
15493                                   html : 'More'
15494                                 }
15495
15496
15497                             ]
15498                         },
15499                         {
15500                             cls: 'ui-resizable-handle ui-resizable-e',
15501                             html : '&nbsp;&nbsp;&nbsp'
15502                         }
15503
15504                     ]
15505                 };
15506
15507                 var ctr = _this.el.select('.fc-event-container',true).first();
15508                 var cg = ctr.createChild(cfg);
15509
15510                 var sbox = c.select('.fc-day-content',true).first().getBox();
15511                 var ebox = c.select('.fc-day-content',true).first().getBox();
15512                 //Roo.log(cg);
15513                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15514                 cg.setWidth(ebox.right - sbox.x -2);
15515
15516                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15517                 
15518             }
15519             
15520         });
15521         
15522         
15523         
15524     },
15525     
15526     onEventEnter: function (e, el,event,d) {
15527         this.fireEvent('evententer', this, el, event);
15528     },
15529     
15530     onEventLeave: function (e, el,event,d) {
15531         this.fireEvent('eventleave', this, el, event);
15532     },
15533     
15534     onEventClick: function (e, el,event,d) {
15535         this.fireEvent('eventclick', this, el, event);
15536     },
15537     
15538     onMonthChange: function () {
15539         this.store.load();
15540     },
15541     
15542     onMoreEventClick: function(e, el, more)
15543     {
15544         var _this = this;
15545         
15546         this.calpopover.placement = 'right';
15547         this.calpopover.setTitle('More');
15548         
15549         this.calpopover.setContent('');
15550         
15551         var ctr = this.calpopover.el.select('.popover-content', true).first();
15552         
15553         Roo.each(more, function(m){
15554             var cfg = {
15555                 cls : 'fc-event-hori fc-event-draggable',
15556                 html : m.title
15557             };
15558             var cg = ctr.createChild(cfg);
15559             
15560             cg.on('click', _this.onEventClick, _this, m);
15561         });
15562         
15563         this.calpopover.show(el);
15564         
15565         
15566     },
15567     
15568     onLoad: function () 
15569     {   
15570         this.calevents = [];
15571         var cal = this;
15572         
15573         if(this.store.getCount() > 0){
15574             this.store.data.each(function(d){
15575                cal.addItem({
15576                     id : d.data.id,
15577                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15578                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15579                     time : d.data.start_time,
15580                     title : d.data.title,
15581                     description : d.data.description,
15582                     venue : d.data.venue
15583                 });
15584             });
15585         }
15586         
15587         this.renderEvents();
15588         
15589         if(this.calevents.length && this.loadMask){
15590             this.maskEl.hide();
15591         }
15592     },
15593     
15594     onBeforeLoad: function()
15595     {
15596         this.clearEvents();
15597         if(this.loadMask){
15598             this.maskEl.show();
15599         }
15600     }
15601 });
15602
15603  
15604  /*
15605  * - LGPL
15606  *
15607  * element
15608  * 
15609  */
15610
15611 /**
15612  * @class Roo.bootstrap.Popover
15613  * @extends Roo.bootstrap.Component
15614  * Bootstrap Popover class
15615  * @cfg {String} html contents of the popover   (or false to use children..)
15616  * @cfg {String} title of popover (or false to hide)
15617  * @cfg {String} placement how it is placed
15618  * @cfg {String} trigger click || hover (or false to trigger manually)
15619  * @cfg {String} over what (parent or false to trigger manually.)
15620  * @cfg {Number} delay - delay before showing
15621  
15622  * @constructor
15623  * Create a new Popover
15624  * @param {Object} config The config object
15625  */
15626
15627 Roo.bootstrap.Popover = function(config){
15628     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15629 };
15630
15631 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15632     
15633     title: 'Fill in a title',
15634     html: false,
15635     
15636     placement : 'right',
15637     trigger : 'hover', // hover
15638     
15639     delay : 0,
15640     
15641     over: 'parent',
15642     
15643     can_build_overlaid : false,
15644     
15645     getChildContainer : function()
15646     {
15647         return this.el.select('.popover-content',true).first();
15648     },
15649     
15650     getAutoCreate : function(){
15651          Roo.log('make popover?');
15652         var cfg = {
15653            cls : 'popover roo-dynamic',
15654            style: 'display:block',
15655            cn : [
15656                 {
15657                     cls : 'arrow'
15658                 },
15659                 {
15660                     cls : 'popover-inner',
15661                     cn : [
15662                         {
15663                             tag: 'h3',
15664                             cls: 'popover-title',
15665                             html : this.title
15666                         },
15667                         {
15668                             cls : 'popover-content',
15669                             html : this.html
15670                         }
15671                     ]
15672                     
15673                 }
15674            ]
15675         };
15676         
15677         return cfg;
15678     },
15679     setTitle: function(str)
15680     {
15681         this.title = str;
15682         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15683     },
15684     setContent: function(str)
15685     {
15686         this.html = str;
15687         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15688     },
15689     // as it get's added to the bottom of the page.
15690     onRender : function(ct, position)
15691     {
15692         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15693         if(!this.el){
15694             var cfg = Roo.apply({},  this.getAutoCreate());
15695             cfg.id = Roo.id();
15696             
15697             if (this.cls) {
15698                 cfg.cls += ' ' + this.cls;
15699             }
15700             if (this.style) {
15701                 cfg.style = this.style;
15702             }
15703             //Roo.log("adding to ");
15704             this.el = Roo.get(document.body).createChild(cfg, position);
15705             Roo.log(this.el);
15706         }
15707         this.initEvents();
15708     },
15709     
15710     initEvents : function()
15711     {
15712         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15713         this.el.enableDisplayMode('block');
15714         this.el.hide();
15715         if (this.over === false) {
15716             return; 
15717         }
15718         if (this.triggers === false) {
15719             return;
15720         }
15721         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15722         var triggers = this.trigger ? this.trigger.split(' ') : [];
15723         Roo.each(triggers, function(trigger) {
15724         
15725             if (trigger == 'click') {
15726                 on_el.on('click', this.toggle, this);
15727             } else if (trigger != 'manual') {
15728                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15729                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15730       
15731                 on_el.on(eventIn  ,this.enter, this);
15732                 on_el.on(eventOut, this.leave, this);
15733             }
15734         }, this);
15735         
15736     },
15737     
15738     
15739     // private
15740     timeout : null,
15741     hoverState : null,
15742     
15743     toggle : function () {
15744         this.hoverState == 'in' ? this.leave() : this.enter();
15745     },
15746     
15747     enter : function () {
15748        
15749     
15750         clearTimeout(this.timeout);
15751     
15752         this.hoverState = 'in';
15753     
15754         if (!this.delay || !this.delay.show) {
15755             this.show();
15756             return;
15757         }
15758         var _t = this;
15759         this.timeout = setTimeout(function () {
15760             if (_t.hoverState == 'in') {
15761                 _t.show();
15762             }
15763         }, this.delay.show)
15764     },
15765     leave : function() {
15766         clearTimeout(this.timeout);
15767     
15768         this.hoverState = 'out';
15769     
15770         if (!this.delay || !this.delay.hide) {
15771             this.hide();
15772             return;
15773         }
15774         var _t = this;
15775         this.timeout = setTimeout(function () {
15776             if (_t.hoverState == 'out') {
15777                 _t.hide();
15778             }
15779         }, this.delay.hide)
15780     },
15781     
15782     show : function (on_el)
15783     {
15784         if (!on_el) {
15785             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15786         }
15787         // set content.
15788         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15789         if (this.html !== false) {
15790             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15791         }
15792         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15793         if (!this.title.length) {
15794             this.el.select('.popover-title',true).hide();
15795         }
15796         
15797         var placement = typeof this.placement == 'function' ?
15798             this.placement.call(this, this.el, on_el) :
15799             this.placement;
15800             
15801         var autoToken = /\s?auto?\s?/i;
15802         var autoPlace = autoToken.test(placement);
15803         if (autoPlace) {
15804             placement = placement.replace(autoToken, '') || 'top';
15805         }
15806         
15807         //this.el.detach()
15808         //this.el.setXY([0,0]);
15809         this.el.show();
15810         this.el.dom.style.display='block';
15811         this.el.addClass(placement);
15812         
15813         //this.el.appendTo(on_el);
15814         
15815         var p = this.getPosition();
15816         var box = this.el.getBox();
15817         
15818         if (autoPlace) {
15819             // fixme..
15820         }
15821         var align = Roo.bootstrap.Popover.alignment[placement];
15822         this.el.alignTo(on_el, align[0],align[1]);
15823         //var arrow = this.el.select('.arrow',true).first();
15824         //arrow.set(align[2], 
15825         
15826         this.el.addClass('in');
15827         
15828         
15829         if (this.el.hasClass('fade')) {
15830             // fade it?
15831         }
15832         
15833     },
15834     hide : function()
15835     {
15836         this.el.setXY([0,0]);
15837         this.el.removeClass('in');
15838         this.el.hide();
15839         this.hoverState = null;
15840         
15841     }
15842     
15843 });
15844
15845 Roo.bootstrap.Popover.alignment = {
15846     'left' : ['r-l', [-10,0], 'right'],
15847     'right' : ['l-r', [10,0], 'left'],
15848     'bottom' : ['t-b', [0,10], 'top'],
15849     'top' : [ 'b-t', [0,-10], 'bottom']
15850 };
15851
15852  /*
15853  * - LGPL
15854  *
15855  * Progress
15856  * 
15857  */
15858
15859 /**
15860  * @class Roo.bootstrap.Progress
15861  * @extends Roo.bootstrap.Component
15862  * Bootstrap Progress class
15863  * @cfg {Boolean} striped striped of the progress bar
15864  * @cfg {Boolean} active animated of the progress bar
15865  * 
15866  * 
15867  * @constructor
15868  * Create a new Progress
15869  * @param {Object} config The config object
15870  */
15871
15872 Roo.bootstrap.Progress = function(config){
15873     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15874 };
15875
15876 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15877     
15878     striped : false,
15879     active: false,
15880     
15881     getAutoCreate : function(){
15882         var cfg = {
15883             tag: 'div',
15884             cls: 'progress'
15885         };
15886         
15887         
15888         if(this.striped){
15889             cfg.cls += ' progress-striped';
15890         }
15891       
15892         if(this.active){
15893             cfg.cls += ' active';
15894         }
15895         
15896         
15897         return cfg;
15898     }
15899    
15900 });
15901
15902  
15903
15904  /*
15905  * - LGPL
15906  *
15907  * ProgressBar
15908  * 
15909  */
15910
15911 /**
15912  * @class Roo.bootstrap.ProgressBar
15913  * @extends Roo.bootstrap.Component
15914  * Bootstrap ProgressBar class
15915  * @cfg {Number} aria_valuenow aria-value now
15916  * @cfg {Number} aria_valuemin aria-value min
15917  * @cfg {Number} aria_valuemax aria-value max
15918  * @cfg {String} label label for the progress bar
15919  * @cfg {String} panel (success | info | warning | danger )
15920  * @cfg {String} role role of the progress bar
15921  * @cfg {String} sr_only text
15922  * 
15923  * 
15924  * @constructor
15925  * Create a new ProgressBar
15926  * @param {Object} config The config object
15927  */
15928
15929 Roo.bootstrap.ProgressBar = function(config){
15930     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15931 };
15932
15933 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15934     
15935     aria_valuenow : 0,
15936     aria_valuemin : 0,
15937     aria_valuemax : 100,
15938     label : false,
15939     panel : false,
15940     role : false,
15941     sr_only: false,
15942     
15943     getAutoCreate : function()
15944     {
15945         
15946         var cfg = {
15947             tag: 'div',
15948             cls: 'progress-bar',
15949             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15950         };
15951         
15952         if(this.sr_only){
15953             cfg.cn = {
15954                 tag: 'span',
15955                 cls: 'sr-only',
15956                 html: this.sr_only
15957             }
15958         }
15959         
15960         if(this.role){
15961             cfg.role = this.role;
15962         }
15963         
15964         if(this.aria_valuenow){
15965             cfg['aria-valuenow'] = this.aria_valuenow;
15966         }
15967         
15968         if(this.aria_valuemin){
15969             cfg['aria-valuemin'] = this.aria_valuemin;
15970         }
15971         
15972         if(this.aria_valuemax){
15973             cfg['aria-valuemax'] = this.aria_valuemax;
15974         }
15975         
15976         if(this.label && !this.sr_only){
15977             cfg.html = this.label;
15978         }
15979         
15980         if(this.panel){
15981             cfg.cls += ' progress-bar-' + this.panel;
15982         }
15983         
15984         return cfg;
15985     },
15986     
15987     update : function(aria_valuenow)
15988     {
15989         this.aria_valuenow = aria_valuenow;
15990         
15991         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15992     }
15993    
15994 });
15995
15996  
15997
15998  /*
15999  * - LGPL
16000  *
16001  * column
16002  * 
16003  */
16004
16005 /**
16006  * @class Roo.bootstrap.TabGroup
16007  * @extends Roo.bootstrap.Column
16008  * Bootstrap Column class
16009  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16010  * @cfg {Boolean} carousel true to make the group behave like a carousel
16011  * @cfg {Boolean} bullets show bullets for the panels
16012  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16013  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16014  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16015  * 
16016  * @constructor
16017  * Create a new TabGroup
16018  * @param {Object} config The config object
16019  */
16020
16021 Roo.bootstrap.TabGroup = function(config){
16022     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16023     if (!this.navId) {
16024         this.navId = Roo.id();
16025     }
16026     this.tabs = [];
16027     Roo.bootstrap.TabGroup.register(this);
16028     
16029 };
16030
16031 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16032     
16033     carousel : false,
16034     transition : false,
16035     bullets : 0,
16036     timer : 0,
16037     autoslide : false,
16038     slideFn : false,
16039     slideOnTouch : false,
16040     
16041     getAutoCreate : function()
16042     {
16043         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16044         
16045         cfg.cls += ' tab-content';
16046         
16047         Roo.log('get auto create...............');
16048         
16049         if (this.carousel) {
16050             cfg.cls += ' carousel slide';
16051             
16052             cfg.cn = [{
16053                cls : 'carousel-inner'
16054             }];
16055         
16056             if(this.bullets  && !Roo.isTouch){
16057                 
16058                 var bullets = {
16059                     cls : 'carousel-bullets',
16060                     cn : []
16061                 };
16062                
16063                 if(this.bullets_cls){
16064                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16065                 }
16066                  /*
16067                 for (var i = 0; i < this.bullets; i++){
16068                     bullets.cn.push({
16069                         cls : 'bullet bullet-' + i
16070                     });
16071                 }
16072                 */
16073                 bullets.cn.push({
16074                     cls : 'clear'
16075                 });
16076                 
16077                 cfg.cn[0].cn = bullets;
16078             }
16079         }
16080         
16081         return cfg;
16082     },
16083     
16084     initEvents:  function()
16085     {
16086         Roo.log('-------- init events on tab group ---------');
16087         
16088         
16089         
16090         Roo.log(this);
16091         
16092         if(Roo.isTouch && this.slideOnTouch){
16093             this.el.on("touchstart", this.onTouchStart, this);
16094         }
16095         
16096         if(this.autoslide){
16097             var _this = this;
16098             
16099             this.slideFn = window.setInterval(function() {
16100                 _this.showPanelNext();
16101             }, this.timer);
16102         }
16103         
16104     },
16105     
16106     onTouchStart : function(e, el, o)
16107     {
16108         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16109             return;
16110         }
16111         
16112         this.showPanelNext();
16113     },
16114     
16115     getChildContainer : function()
16116     {
16117         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16118     },
16119     
16120     /**
16121     * register a Navigation item
16122     * @param {Roo.bootstrap.NavItem} the navitem to add
16123     */
16124     register : function(item)
16125     {
16126         this.tabs.push( item);
16127         item.navId = this.navId; // not really needed..
16128         this.addBullet();
16129     
16130     },
16131     
16132     getActivePanel : function()
16133     {
16134         var r = false;
16135         Roo.each(this.tabs, function(t) {
16136             if (t.active) {
16137                 r = t;
16138                 return false;
16139             }
16140             return null;
16141         });
16142         return r;
16143         
16144     },
16145     getPanelByName : function(n)
16146     {
16147         var r = false;
16148         Roo.each(this.tabs, function(t) {
16149             if (t.tabId == n) {
16150                 r = t;
16151                 return false;
16152             }
16153             return null;
16154         });
16155         return r;
16156     },
16157     indexOfPanel : function(p)
16158     {
16159         var r = false;
16160         Roo.each(this.tabs, function(t,i) {
16161             if (t.tabId == p.tabId) {
16162                 r = i;
16163                 return false;
16164             }
16165             return null;
16166         });
16167         return r;
16168     },
16169     /**
16170      * show a specific panel
16171      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16172      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16173      */
16174     showPanel : function (pan)
16175     {
16176         if(this.transition){
16177             Roo.log("waiting for the transitionend");
16178             return;
16179         }
16180         
16181         if (typeof(pan) == 'number') {
16182             pan = this.tabs[pan];
16183         }
16184         if (typeof(pan) == 'string') {
16185             pan = this.getPanelByName(pan);
16186         }
16187         if (pan.tabId == this.getActivePanel().tabId) {
16188             return true;
16189         }
16190         var cur = this.getActivePanel();
16191         
16192         if (false === cur.fireEvent('beforedeactivate')) {
16193             return false;
16194         }
16195         
16196         if(this.bullets > 0 && !Roo.isTouch){
16197             this.setActiveBullet(this.indexOfPanel(pan));
16198         }
16199         
16200         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16201             
16202             this.transition = true;
16203             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16204             var lr = dir == 'next' ? 'left' : 'right';
16205             pan.el.addClass(dir); // or prev
16206             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16207             cur.el.addClass(lr); // or right
16208             pan.el.addClass(lr);
16209             
16210             var _this = this;
16211             cur.el.on('transitionend', function() {
16212                 Roo.log("trans end?");
16213                 
16214                 pan.el.removeClass([lr,dir]);
16215                 pan.setActive(true);
16216                 
16217                 cur.el.removeClass([lr]);
16218                 cur.setActive(false);
16219                 
16220                 _this.transition = false;
16221                 
16222             }, this, { single:  true } );
16223             
16224             return true;
16225         }
16226         
16227         cur.setActive(false);
16228         pan.setActive(true);
16229         
16230         return true;
16231         
16232     },
16233     showPanelNext : function()
16234     {
16235         var i = this.indexOfPanel(this.getActivePanel());
16236         
16237         if (i >= this.tabs.length - 1 && !this.autoslide) {
16238             return;
16239         }
16240         
16241         if (i >= this.tabs.length - 1 && this.autoslide) {
16242             i = -1;
16243         }
16244         
16245         this.showPanel(this.tabs[i+1]);
16246     },
16247     
16248     showPanelPrev : function()
16249     {
16250         var i = this.indexOfPanel(this.getActivePanel());
16251         
16252         if (i  < 1 && !this.autoslide) {
16253             return;
16254         }
16255         
16256         if (i < 1 && this.autoslide) {
16257             i = this.tabs.length;
16258         }
16259         
16260         this.showPanel(this.tabs[i-1]);
16261     },
16262     
16263     
16264     addBullet: function()
16265     {
16266         if(!this.bullets || Roo.isTouch){
16267             return;
16268         }
16269         var ctr = this.el.select('.carousel-bullets',true).first();
16270         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16271         var bullet = ctr.createChild({
16272             cls : 'bullet bullet-' + i
16273         },ctr.dom.lastChild);
16274         
16275         
16276         var _this = this;
16277         
16278         bullet.on('click', (function(e, el, o, ii, t){
16279
16280             e.preventDefault();
16281
16282             this.showPanel(ii);
16283
16284             if(this.autoslide && this.slideFn){
16285                 clearInterval(this.slideFn);
16286                 this.slideFn = window.setInterval(function() {
16287                     _this.showPanelNext();
16288                 }, this.timer);
16289             }
16290
16291         }).createDelegate(this, [i, bullet], true));
16292                 
16293         
16294     },
16295      
16296     setActiveBullet : function(i)
16297     {
16298         if(Roo.isTouch){
16299             return;
16300         }
16301         
16302         Roo.each(this.el.select('.bullet', true).elements, function(el){
16303             el.removeClass('selected');
16304         });
16305
16306         var bullet = this.el.select('.bullet-' + i, true).first();
16307         
16308         if(!bullet){
16309             return;
16310         }
16311         
16312         bullet.addClass('selected');
16313     }
16314     
16315     
16316   
16317 });
16318
16319  
16320
16321  
16322  
16323 Roo.apply(Roo.bootstrap.TabGroup, {
16324     
16325     groups: {},
16326      /**
16327     * register a Navigation Group
16328     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16329     */
16330     register : function(navgrp)
16331     {
16332         this.groups[navgrp.navId] = navgrp;
16333         
16334     },
16335     /**
16336     * fetch a Navigation Group based on the navigation ID
16337     * if one does not exist , it will get created.
16338     * @param {string} the navgroup to add
16339     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16340     */
16341     get: function(navId) {
16342         if (typeof(this.groups[navId]) == 'undefined') {
16343             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16344         }
16345         return this.groups[navId] ;
16346     }
16347     
16348     
16349     
16350 });
16351
16352  /*
16353  * - LGPL
16354  *
16355  * TabPanel
16356  * 
16357  */
16358
16359 /**
16360  * @class Roo.bootstrap.TabPanel
16361  * @extends Roo.bootstrap.Component
16362  * Bootstrap TabPanel class
16363  * @cfg {Boolean} active panel active
16364  * @cfg {String} html panel content
16365  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16366  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16367  * 
16368  * 
16369  * @constructor
16370  * Create a new TabPanel
16371  * @param {Object} config The config object
16372  */
16373
16374 Roo.bootstrap.TabPanel = function(config){
16375     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16376     this.addEvents({
16377         /**
16378              * @event changed
16379              * Fires when the active status changes
16380              * @param {Roo.bootstrap.TabPanel} this
16381              * @param {Boolean} state the new state
16382             
16383          */
16384         'changed': true,
16385         /**
16386              * @event beforedeactivate
16387              * Fires before a tab is de-activated - can be used to do validation on a form.
16388              * @param {Roo.bootstrap.TabPanel} this
16389              * @return {Boolean} false if there is an error
16390             
16391          */
16392         'beforedeactivate': true
16393      });
16394     
16395     this.tabId = this.tabId || Roo.id();
16396   
16397 };
16398
16399 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16400     
16401     active: false,
16402     html: false,
16403     tabId: false,
16404     navId : false,
16405     
16406     getAutoCreate : function(){
16407         var cfg = {
16408             tag: 'div',
16409             // item is needed for carousel - not sure if it has any effect otherwise
16410             cls: 'tab-pane item',
16411             html: this.html || ''
16412         };
16413         
16414         if(this.active){
16415             cfg.cls += ' active';
16416         }
16417         
16418         if(this.tabId){
16419             cfg.tabId = this.tabId;
16420         }
16421         
16422         
16423         return cfg;
16424     },
16425     
16426     initEvents:  function()
16427     {
16428         Roo.log('-------- init events on tab panel ---------');
16429         
16430         var p = this.parent();
16431         this.navId = this.navId || p.navId;
16432         
16433         if (typeof(this.navId) != 'undefined') {
16434             // not really needed.. but just in case.. parent should be a NavGroup.
16435             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16436             Roo.log(['register', tg, this]);
16437             tg.register(this);
16438             
16439             var i = tg.tabs.length - 1;
16440             
16441             if(this.active && tg.bullets > 0 && i < tg.bullets){
16442                 tg.setActiveBullet(i);
16443             }
16444         }
16445         
16446     },
16447     
16448     
16449     onRender : function(ct, position)
16450     {
16451        // Roo.log("Call onRender: " + this.xtype);
16452         
16453         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16454         
16455         
16456         
16457         
16458         
16459     },
16460     
16461     setActive: function(state)
16462     {
16463         Roo.log("panel - set active " + this.tabId + "=" + state);
16464         
16465         this.active = state;
16466         if (!state) {
16467             this.el.removeClass('active');
16468             
16469         } else  if (!this.el.hasClass('active')) {
16470             this.el.addClass('active');
16471         }
16472         
16473         this.fireEvent('changed', this, state);
16474     }
16475     
16476     
16477 });
16478  
16479
16480  
16481
16482  /*
16483  * - LGPL
16484  *
16485  * DateField
16486  * 
16487  */
16488
16489 /**
16490  * @class Roo.bootstrap.DateField
16491  * @extends Roo.bootstrap.Input
16492  * Bootstrap DateField class
16493  * @cfg {Number} weekStart default 0
16494  * @cfg {String} viewMode default empty, (months|years)
16495  * @cfg {String} minViewMode default empty, (months|years)
16496  * @cfg {Number} startDate default -Infinity
16497  * @cfg {Number} endDate default Infinity
16498  * @cfg {Boolean} todayHighlight default false
16499  * @cfg {Boolean} todayBtn default false
16500  * @cfg {Boolean} calendarWeeks default false
16501  * @cfg {Object} daysOfWeekDisabled default empty
16502  * @cfg {Boolean} singleMode default false (true | false)
16503  * 
16504  * @cfg {Boolean} keyboardNavigation default true
16505  * @cfg {String} language default en
16506  * 
16507  * @constructor
16508  * Create a new DateField
16509  * @param {Object} config The config object
16510  */
16511
16512 Roo.bootstrap.DateField = function(config){
16513     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16514      this.addEvents({
16515             /**
16516              * @event show
16517              * Fires when this field show.
16518              * @param {Roo.bootstrap.DateField} this
16519              * @param {Mixed} date The date value
16520              */
16521             show : true,
16522             /**
16523              * @event show
16524              * Fires when this field hide.
16525              * @param {Roo.bootstrap.DateField} this
16526              * @param {Mixed} date The date value
16527              */
16528             hide : true,
16529             /**
16530              * @event select
16531              * Fires when select a date.
16532              * @param {Roo.bootstrap.DateField} this
16533              * @param {Mixed} date The date value
16534              */
16535             select : true,
16536             /**
16537              * @event beforeselect
16538              * Fires when before select a date.
16539              * @param {Roo.bootstrap.DateField} this
16540              * @param {Mixed} date The date value
16541              */
16542             beforeselect : true
16543         });
16544 };
16545
16546 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16547     
16548     /**
16549      * @cfg {String} format
16550      * The default date format string which can be overriden for localization support.  The format must be
16551      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16552      */
16553     format : "m/d/y",
16554     /**
16555      * @cfg {String} altFormats
16556      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16557      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16558      */
16559     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16560     
16561     weekStart : 0,
16562     
16563     viewMode : '',
16564     
16565     minViewMode : '',
16566     
16567     todayHighlight : false,
16568     
16569     todayBtn: false,
16570     
16571     language: 'en',
16572     
16573     keyboardNavigation: true,
16574     
16575     calendarWeeks: false,
16576     
16577     startDate: -Infinity,
16578     
16579     endDate: Infinity,
16580     
16581     daysOfWeekDisabled: [],
16582     
16583     _events: [],
16584     
16585     singleMode : false,
16586     
16587     UTCDate: function()
16588     {
16589         return new Date(Date.UTC.apply(Date, arguments));
16590     },
16591     
16592     UTCToday: function()
16593     {
16594         var today = new Date();
16595         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16596     },
16597     
16598     getDate: function() {
16599             var d = this.getUTCDate();
16600             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16601     },
16602     
16603     getUTCDate: function() {
16604             return this.date;
16605     },
16606     
16607     setDate: function(d) {
16608             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16609     },
16610     
16611     setUTCDate: function(d) {
16612             this.date = d;
16613             this.setValue(this.formatDate(this.date));
16614     },
16615         
16616     onRender: function(ct, position)
16617     {
16618         
16619         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16620         
16621         this.language = this.language || 'en';
16622         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16623         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16624         
16625         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16626         this.format = this.format || 'm/d/y';
16627         this.isInline = false;
16628         this.isInput = true;
16629         this.component = this.el.select('.add-on', true).first() || false;
16630         this.component = (this.component && this.component.length === 0) ? false : this.component;
16631         this.hasInput = this.component && this.inputEL().length;
16632         
16633         if (typeof(this.minViewMode === 'string')) {
16634             switch (this.minViewMode) {
16635                 case 'months':
16636                     this.minViewMode = 1;
16637                     break;
16638                 case 'years':
16639                     this.minViewMode = 2;
16640                     break;
16641                 default:
16642                     this.minViewMode = 0;
16643                     break;
16644             }
16645         }
16646         
16647         if (typeof(this.viewMode === 'string')) {
16648             switch (this.viewMode) {
16649                 case 'months':
16650                     this.viewMode = 1;
16651                     break;
16652                 case 'years':
16653                     this.viewMode = 2;
16654                     break;
16655                 default:
16656                     this.viewMode = 0;
16657                     break;
16658             }
16659         }
16660                 
16661         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16662         
16663 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16664         
16665         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16666         
16667         this.picker().on('mousedown', this.onMousedown, this);
16668         this.picker().on('click', this.onClick, this);
16669         
16670         this.picker().addClass('datepicker-dropdown');
16671         
16672         this.startViewMode = this.viewMode;
16673         
16674         if(this.singleMode){
16675             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16676                 v.setVisibilityMode(Roo.Element.DISPLAY);
16677                 v.hide();
16678             });
16679             
16680             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16681                 v.setStyle('width', '189px');
16682             });
16683         }
16684         
16685         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16686             if(!this.calendarWeeks){
16687                 v.remove();
16688                 return;
16689             }
16690             
16691             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16692             v.attr('colspan', function(i, val){
16693                 return parseInt(val) + 1;
16694             });
16695         });
16696                         
16697         
16698         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16699         
16700         this.setStartDate(this.startDate);
16701         this.setEndDate(this.endDate);
16702         
16703         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16704         
16705         this.fillDow();
16706         this.fillMonths();
16707         this.update();
16708         this.showMode();
16709         
16710         if(this.isInline) {
16711             this.show();
16712         }
16713     },
16714     
16715     picker : function()
16716     {
16717         return this.pickerEl;
16718 //        return this.el.select('.datepicker', true).first();
16719     },
16720     
16721     fillDow: function()
16722     {
16723         var dowCnt = this.weekStart;
16724         
16725         var dow = {
16726             tag: 'tr',
16727             cn: [
16728                 
16729             ]
16730         };
16731         
16732         if(this.calendarWeeks){
16733             dow.cn.push({
16734                 tag: 'th',
16735                 cls: 'cw',
16736                 html: '&nbsp;'
16737             })
16738         }
16739         
16740         while (dowCnt < this.weekStart + 7) {
16741             dow.cn.push({
16742                 tag: 'th',
16743                 cls: 'dow',
16744                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16745             });
16746         }
16747         
16748         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16749     },
16750     
16751     fillMonths: function()
16752     {    
16753         var i = 0;
16754         var months = this.picker().select('>.datepicker-months td', true).first();
16755         
16756         months.dom.innerHTML = '';
16757         
16758         while (i < 12) {
16759             var month = {
16760                 tag: 'span',
16761                 cls: 'month',
16762                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16763             };
16764             
16765             months.createChild(month);
16766         }
16767         
16768     },
16769     
16770     update: function()
16771     {
16772         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;
16773         
16774         if (this.date < this.startDate) {
16775             this.viewDate = new Date(this.startDate);
16776         } else if (this.date > this.endDate) {
16777             this.viewDate = new Date(this.endDate);
16778         } else {
16779             this.viewDate = new Date(this.date);
16780         }
16781         
16782         this.fill();
16783     },
16784     
16785     fill: function() 
16786     {
16787         var d = new Date(this.viewDate),
16788                 year = d.getUTCFullYear(),
16789                 month = d.getUTCMonth(),
16790                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16791                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16792                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16793                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16794                 currentDate = this.date && this.date.valueOf(),
16795                 today = this.UTCToday();
16796         
16797         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16798         
16799 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16800         
16801 //        this.picker.select('>tfoot th.today').
16802 //                                              .text(dates[this.language].today)
16803 //                                              .toggle(this.todayBtn !== false);
16804     
16805         this.updateNavArrows();
16806         this.fillMonths();
16807                                                 
16808         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16809         
16810         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16811          
16812         prevMonth.setUTCDate(day);
16813         
16814         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16815         
16816         var nextMonth = new Date(prevMonth);
16817         
16818         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16819         
16820         nextMonth = nextMonth.valueOf();
16821         
16822         var fillMonths = false;
16823         
16824         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16825         
16826         while(prevMonth.valueOf() < nextMonth) {
16827             var clsName = '';
16828             
16829             if (prevMonth.getUTCDay() === this.weekStart) {
16830                 if(fillMonths){
16831                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16832                 }
16833                     
16834                 fillMonths = {
16835                     tag: 'tr',
16836                     cn: []
16837                 };
16838                 
16839                 if(this.calendarWeeks){
16840                     // ISO 8601: First week contains first thursday.
16841                     // ISO also states week starts on Monday, but we can be more abstract here.
16842                     var
16843                     // Start of current week: based on weekstart/current date
16844                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16845                     // Thursday of this week
16846                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16847                     // First Thursday of year, year from thursday
16848                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16849                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16850                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16851                     
16852                     fillMonths.cn.push({
16853                         tag: 'td',
16854                         cls: 'cw',
16855                         html: calWeek
16856                     });
16857                 }
16858             }
16859             
16860             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16861                 clsName += ' old';
16862             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16863                 clsName += ' new';
16864             }
16865             if (this.todayHighlight &&
16866                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16867                 prevMonth.getUTCMonth() == today.getMonth() &&
16868                 prevMonth.getUTCDate() == today.getDate()) {
16869                 clsName += ' today';
16870             }
16871             
16872             if (currentDate && prevMonth.valueOf() === currentDate) {
16873                 clsName += ' active';
16874             }
16875             
16876             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16877                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16878                     clsName += ' disabled';
16879             }
16880             
16881             fillMonths.cn.push({
16882                 tag: 'td',
16883                 cls: 'day ' + clsName,
16884                 html: prevMonth.getDate()
16885             });
16886             
16887             prevMonth.setDate(prevMonth.getDate()+1);
16888         }
16889           
16890         var currentYear = this.date && this.date.getUTCFullYear();
16891         var currentMonth = this.date && this.date.getUTCMonth();
16892         
16893         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16894         
16895         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16896             v.removeClass('active');
16897             
16898             if(currentYear === year && k === currentMonth){
16899                 v.addClass('active');
16900             }
16901             
16902             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16903                 v.addClass('disabled');
16904             }
16905             
16906         });
16907         
16908         
16909         year = parseInt(year/10, 10) * 10;
16910         
16911         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16912         
16913         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16914         
16915         year -= 1;
16916         for (var i = -1; i < 11; i++) {
16917             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16918                 tag: 'span',
16919                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16920                 html: year
16921             });
16922             
16923             year += 1;
16924         }
16925     },
16926     
16927     showMode: function(dir) 
16928     {
16929         if (dir) {
16930             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16931         }
16932         
16933         Roo.each(this.picker().select('>div',true).elements, function(v){
16934             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16935             v.hide();
16936         });
16937         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16938     },
16939     
16940     place: function()
16941     {
16942         if(this.isInline) {
16943             return;
16944         }
16945         
16946         this.picker().removeClass(['bottom', 'top']);
16947         
16948         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16949             /*
16950              * place to the top of element!
16951              *
16952              */
16953             
16954             this.picker().addClass('top');
16955             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16956             
16957             return;
16958         }
16959         
16960         this.picker().addClass('bottom');
16961         
16962         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16963     },
16964     
16965     parseDate : function(value)
16966     {
16967         if(!value || value instanceof Date){
16968             return value;
16969         }
16970         var v = Date.parseDate(value, this.format);
16971         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16972             v = Date.parseDate(value, 'Y-m-d');
16973         }
16974         if(!v && this.altFormats){
16975             if(!this.altFormatsArray){
16976                 this.altFormatsArray = this.altFormats.split("|");
16977             }
16978             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16979                 v = Date.parseDate(value, this.altFormatsArray[i]);
16980             }
16981         }
16982         return v;
16983     },
16984     
16985     formatDate : function(date, fmt)
16986     {   
16987         return (!date || !(date instanceof Date)) ?
16988         date : date.dateFormat(fmt || this.format);
16989     },
16990     
16991     onFocus : function()
16992     {
16993         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16994         this.show();
16995     },
16996     
16997     onBlur : function()
16998     {
16999         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17000         
17001         var d = this.inputEl().getValue();
17002         
17003         this.setValue(d);
17004                 
17005         this.hide();
17006     },
17007     
17008     show : function()
17009     {
17010         this.picker().show();
17011         this.update();
17012         this.place();
17013         
17014         this.fireEvent('show', this, this.date);
17015     },
17016     
17017     hide : function()
17018     {
17019         if(this.isInline) {
17020             return;
17021         }
17022         this.picker().hide();
17023         this.viewMode = this.startViewMode;
17024         this.showMode();
17025         
17026         this.fireEvent('hide', this, this.date);
17027         
17028     },
17029     
17030     onMousedown: function(e)
17031     {
17032         e.stopPropagation();
17033         e.preventDefault();
17034     },
17035     
17036     keyup: function(e)
17037     {
17038         Roo.bootstrap.DateField.superclass.keyup.call(this);
17039         this.update();
17040     },
17041
17042     setValue: function(v)
17043     {
17044         if(this.fireEvent('beforeselect', this, v) !== false){
17045             var d = new Date(this.parseDate(v) ).clearTime();
17046         
17047             if(isNaN(d.getTime())){
17048                 this.date = this.viewDate = '';
17049                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17050                 return;
17051             }
17052
17053             v = this.formatDate(d);
17054
17055             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17056
17057             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17058
17059             this.update();
17060
17061             this.fireEvent('select', this, this.date);
17062         }
17063     },
17064     
17065     getValue: function()
17066     {
17067         return this.formatDate(this.date);
17068     },
17069     
17070     fireKey: function(e)
17071     {
17072         if (!this.picker().isVisible()){
17073             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17074                 this.show();
17075             }
17076             return;
17077         }
17078         
17079         var dateChanged = false,
17080         dir, day, month,
17081         newDate, newViewDate;
17082         
17083         switch(e.keyCode){
17084             case 27: // escape
17085                 this.hide();
17086                 e.preventDefault();
17087                 break;
17088             case 37: // left
17089             case 39: // right
17090                 if (!this.keyboardNavigation) {
17091                     break;
17092                 }
17093                 dir = e.keyCode == 37 ? -1 : 1;
17094                 
17095                 if (e.ctrlKey){
17096                     newDate = this.moveYear(this.date, dir);
17097                     newViewDate = this.moveYear(this.viewDate, dir);
17098                 } else if (e.shiftKey){
17099                     newDate = this.moveMonth(this.date, dir);
17100                     newViewDate = this.moveMonth(this.viewDate, dir);
17101                 } else {
17102                     newDate = new Date(this.date);
17103                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17104                     newViewDate = new Date(this.viewDate);
17105                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17106                 }
17107                 if (this.dateWithinRange(newDate)){
17108                     this.date = newDate;
17109                     this.viewDate = newViewDate;
17110                     this.setValue(this.formatDate(this.date));
17111 //                    this.update();
17112                     e.preventDefault();
17113                     dateChanged = true;
17114                 }
17115                 break;
17116             case 38: // up
17117             case 40: // down
17118                 if (!this.keyboardNavigation) {
17119                     break;
17120                 }
17121                 dir = e.keyCode == 38 ? -1 : 1;
17122                 if (e.ctrlKey){
17123                     newDate = this.moveYear(this.date, dir);
17124                     newViewDate = this.moveYear(this.viewDate, dir);
17125                 } else if (e.shiftKey){
17126                     newDate = this.moveMonth(this.date, dir);
17127                     newViewDate = this.moveMonth(this.viewDate, dir);
17128                 } else {
17129                     newDate = new Date(this.date);
17130                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17131                     newViewDate = new Date(this.viewDate);
17132                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17133                 }
17134                 if (this.dateWithinRange(newDate)){
17135                     this.date = newDate;
17136                     this.viewDate = newViewDate;
17137                     this.setValue(this.formatDate(this.date));
17138 //                    this.update();
17139                     e.preventDefault();
17140                     dateChanged = true;
17141                 }
17142                 break;
17143             case 13: // enter
17144                 this.setValue(this.formatDate(this.date));
17145                 this.hide();
17146                 e.preventDefault();
17147                 break;
17148             case 9: // tab
17149                 this.setValue(this.formatDate(this.date));
17150                 this.hide();
17151                 break;
17152             case 16: // shift
17153             case 17: // ctrl
17154             case 18: // alt
17155                 break;
17156             default :
17157                 this.hide();
17158                 
17159         }
17160     },
17161     
17162     
17163     onClick: function(e) 
17164     {
17165         e.stopPropagation();
17166         e.preventDefault();
17167         
17168         var target = e.getTarget();
17169         
17170         if(target.nodeName.toLowerCase() === 'i'){
17171             target = Roo.get(target).dom.parentNode;
17172         }
17173         
17174         var nodeName = target.nodeName;
17175         var className = target.className;
17176         var html = target.innerHTML;
17177         //Roo.log(nodeName);
17178         
17179         switch(nodeName.toLowerCase()) {
17180             case 'th':
17181                 switch(className) {
17182                     case 'switch':
17183                         this.showMode(1);
17184                         break;
17185                     case 'prev':
17186                     case 'next':
17187                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17188                         switch(this.viewMode){
17189                                 case 0:
17190                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17191                                         break;
17192                                 case 1:
17193                                 case 2:
17194                                         this.viewDate = this.moveYear(this.viewDate, dir);
17195                                         break;
17196                         }
17197                         this.fill();
17198                         break;
17199                     case 'today':
17200                         var date = new Date();
17201                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17202 //                        this.fill()
17203                         this.setValue(this.formatDate(this.date));
17204                         
17205                         this.hide();
17206                         break;
17207                 }
17208                 break;
17209             case 'span':
17210                 if (className.indexOf('disabled') < 0) {
17211                     this.viewDate.setUTCDate(1);
17212                     if (className.indexOf('month') > -1) {
17213                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17214                     } else {
17215                         var year = parseInt(html, 10) || 0;
17216                         this.viewDate.setUTCFullYear(year);
17217                         
17218                     }
17219                     
17220                     if(this.singleMode){
17221                         this.setValue(this.formatDate(this.viewDate));
17222                         this.hide();
17223                         return;
17224                     }
17225                     
17226                     this.showMode(-1);
17227                     this.fill();
17228                 }
17229                 break;
17230                 
17231             case 'td':
17232                 //Roo.log(className);
17233                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17234                     var day = parseInt(html, 10) || 1;
17235                     var year = this.viewDate.getUTCFullYear(),
17236                         month = this.viewDate.getUTCMonth();
17237
17238                     if (className.indexOf('old') > -1) {
17239                         if(month === 0 ){
17240                             month = 11;
17241                             year -= 1;
17242                         }else{
17243                             month -= 1;
17244                         }
17245                     } else if (className.indexOf('new') > -1) {
17246                         if (month == 11) {
17247                             month = 0;
17248                             year += 1;
17249                         } else {
17250                             month += 1;
17251                         }
17252                     }
17253                     //Roo.log([year,month,day]);
17254                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17255                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17256 //                    this.fill();
17257                     //Roo.log(this.formatDate(this.date));
17258                     this.setValue(this.formatDate(this.date));
17259                     this.hide();
17260                 }
17261                 break;
17262         }
17263     },
17264     
17265     setStartDate: function(startDate)
17266     {
17267         this.startDate = startDate || -Infinity;
17268         if (this.startDate !== -Infinity) {
17269             this.startDate = this.parseDate(this.startDate);
17270         }
17271         this.update();
17272         this.updateNavArrows();
17273     },
17274
17275     setEndDate: function(endDate)
17276     {
17277         this.endDate = endDate || Infinity;
17278         if (this.endDate !== Infinity) {
17279             this.endDate = this.parseDate(this.endDate);
17280         }
17281         this.update();
17282         this.updateNavArrows();
17283     },
17284     
17285     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17286     {
17287         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17288         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17289             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17290         }
17291         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17292             return parseInt(d, 10);
17293         });
17294         this.update();
17295         this.updateNavArrows();
17296     },
17297     
17298     updateNavArrows: function() 
17299     {
17300         if(this.singleMode){
17301             return;
17302         }
17303         
17304         var d = new Date(this.viewDate),
17305         year = d.getUTCFullYear(),
17306         month = d.getUTCMonth();
17307         
17308         Roo.each(this.picker().select('.prev', true).elements, function(v){
17309             v.show();
17310             switch (this.viewMode) {
17311                 case 0:
17312
17313                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17314                         v.hide();
17315                     }
17316                     break;
17317                 case 1:
17318                 case 2:
17319                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17320                         v.hide();
17321                     }
17322                     break;
17323             }
17324         });
17325         
17326         Roo.each(this.picker().select('.next', true).elements, function(v){
17327             v.show();
17328             switch (this.viewMode) {
17329                 case 0:
17330
17331                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17332                         v.hide();
17333                     }
17334                     break;
17335                 case 1:
17336                 case 2:
17337                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17338                         v.hide();
17339                     }
17340                     break;
17341             }
17342         })
17343     },
17344     
17345     moveMonth: function(date, dir)
17346     {
17347         if (!dir) {
17348             return date;
17349         }
17350         var new_date = new Date(date.valueOf()),
17351         day = new_date.getUTCDate(),
17352         month = new_date.getUTCMonth(),
17353         mag = Math.abs(dir),
17354         new_month, test;
17355         dir = dir > 0 ? 1 : -1;
17356         if (mag == 1){
17357             test = dir == -1
17358             // If going back one month, make sure month is not current month
17359             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17360             ? function(){
17361                 return new_date.getUTCMonth() == month;
17362             }
17363             // If going forward one month, make sure month is as expected
17364             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17365             : function(){
17366                 return new_date.getUTCMonth() != new_month;
17367             };
17368             new_month = month + dir;
17369             new_date.setUTCMonth(new_month);
17370             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17371             if (new_month < 0 || new_month > 11) {
17372                 new_month = (new_month + 12) % 12;
17373             }
17374         } else {
17375             // For magnitudes >1, move one month at a time...
17376             for (var i=0; i<mag; i++) {
17377                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17378                 new_date = this.moveMonth(new_date, dir);
17379             }
17380             // ...then reset the day, keeping it in the new month
17381             new_month = new_date.getUTCMonth();
17382             new_date.setUTCDate(day);
17383             test = function(){
17384                 return new_month != new_date.getUTCMonth();
17385             };
17386         }
17387         // Common date-resetting loop -- if date is beyond end of month, make it
17388         // end of month
17389         while (test()){
17390             new_date.setUTCDate(--day);
17391             new_date.setUTCMonth(new_month);
17392         }
17393         return new_date;
17394     },
17395
17396     moveYear: function(date, dir)
17397     {
17398         return this.moveMonth(date, dir*12);
17399     },
17400
17401     dateWithinRange: function(date)
17402     {
17403         return date >= this.startDate && date <= this.endDate;
17404     },
17405
17406     
17407     remove: function() 
17408     {
17409         this.picker().remove();
17410     }
17411    
17412 });
17413
17414 Roo.apply(Roo.bootstrap.DateField,  {
17415     
17416     head : {
17417         tag: 'thead',
17418         cn: [
17419         {
17420             tag: 'tr',
17421             cn: [
17422             {
17423                 tag: 'th',
17424                 cls: 'prev',
17425                 html: '<i class="fa fa-arrow-left"/>'
17426             },
17427             {
17428                 tag: 'th',
17429                 cls: 'switch',
17430                 colspan: '5'
17431             },
17432             {
17433                 tag: 'th',
17434                 cls: 'next',
17435                 html: '<i class="fa fa-arrow-right"/>'
17436             }
17437
17438             ]
17439         }
17440         ]
17441     },
17442     
17443     content : {
17444         tag: 'tbody',
17445         cn: [
17446         {
17447             tag: 'tr',
17448             cn: [
17449             {
17450                 tag: 'td',
17451                 colspan: '7'
17452             }
17453             ]
17454         }
17455         ]
17456     },
17457     
17458     footer : {
17459         tag: 'tfoot',
17460         cn: [
17461         {
17462             tag: 'tr',
17463             cn: [
17464             {
17465                 tag: 'th',
17466                 colspan: '7',
17467                 cls: 'today'
17468             }
17469                     
17470             ]
17471         }
17472         ]
17473     },
17474     
17475     dates:{
17476         en: {
17477             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17478             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17479             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17480             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17481             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17482             today: "Today"
17483         }
17484     },
17485     
17486     modes: [
17487     {
17488         clsName: 'days',
17489         navFnc: 'Month',
17490         navStep: 1
17491     },
17492     {
17493         clsName: 'months',
17494         navFnc: 'FullYear',
17495         navStep: 1
17496     },
17497     {
17498         clsName: 'years',
17499         navFnc: 'FullYear',
17500         navStep: 10
17501     }]
17502 });
17503
17504 Roo.apply(Roo.bootstrap.DateField,  {
17505   
17506     template : {
17507         tag: 'div',
17508         cls: 'datepicker dropdown-menu roo-dynamic',
17509         cn: [
17510         {
17511             tag: 'div',
17512             cls: 'datepicker-days',
17513             cn: [
17514             {
17515                 tag: 'table',
17516                 cls: 'table-condensed',
17517                 cn:[
17518                 Roo.bootstrap.DateField.head,
17519                 {
17520                     tag: 'tbody'
17521                 },
17522                 Roo.bootstrap.DateField.footer
17523                 ]
17524             }
17525             ]
17526         },
17527         {
17528             tag: 'div',
17529             cls: 'datepicker-months',
17530             cn: [
17531             {
17532                 tag: 'table',
17533                 cls: 'table-condensed',
17534                 cn:[
17535                 Roo.bootstrap.DateField.head,
17536                 Roo.bootstrap.DateField.content,
17537                 Roo.bootstrap.DateField.footer
17538                 ]
17539             }
17540             ]
17541         },
17542         {
17543             tag: 'div',
17544             cls: 'datepicker-years',
17545             cn: [
17546             {
17547                 tag: 'table',
17548                 cls: 'table-condensed',
17549                 cn:[
17550                 Roo.bootstrap.DateField.head,
17551                 Roo.bootstrap.DateField.content,
17552                 Roo.bootstrap.DateField.footer
17553                 ]
17554             }
17555             ]
17556         }
17557         ]
17558     }
17559 });
17560
17561  
17562
17563  /*
17564  * - LGPL
17565  *
17566  * TimeField
17567  * 
17568  */
17569
17570 /**
17571  * @class Roo.bootstrap.TimeField
17572  * @extends Roo.bootstrap.Input
17573  * Bootstrap DateField class
17574  * 
17575  * 
17576  * @constructor
17577  * Create a new TimeField
17578  * @param {Object} config The config object
17579  */
17580
17581 Roo.bootstrap.TimeField = function(config){
17582     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17583     this.addEvents({
17584             /**
17585              * @event show
17586              * Fires when this field show.
17587              * @param {Roo.bootstrap.DateField} thisthis
17588              * @param {Mixed} date The date value
17589              */
17590             show : true,
17591             /**
17592              * @event show
17593              * Fires when this field hide.
17594              * @param {Roo.bootstrap.DateField} this
17595              * @param {Mixed} date The date value
17596              */
17597             hide : true,
17598             /**
17599              * @event select
17600              * Fires when select a date.
17601              * @param {Roo.bootstrap.DateField} this
17602              * @param {Mixed} date The date value
17603              */
17604             select : true
17605         });
17606 };
17607
17608 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17609     
17610     /**
17611      * @cfg {String} format
17612      * The default time format string which can be overriden for localization support.  The format must be
17613      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17614      */
17615     format : "H:i",
17616        
17617     onRender: function(ct, position)
17618     {
17619         
17620         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17621                 
17622         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17623         
17624         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17625         
17626         this.pop = this.picker().select('>.datepicker-time',true).first();
17627         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17628         
17629         this.picker().on('mousedown', this.onMousedown, this);
17630         this.picker().on('click', this.onClick, this);
17631         
17632         this.picker().addClass('datepicker-dropdown');
17633     
17634         this.fillTime();
17635         this.update();
17636             
17637         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17638         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17639         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17640         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17641         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17642         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17643
17644     },
17645     
17646     fireKey: function(e){
17647         if (!this.picker().isVisible()){
17648             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17649                 this.show();
17650             }
17651             return;
17652         }
17653
17654         e.preventDefault();
17655         
17656         switch(e.keyCode){
17657             case 27: // escape
17658                 this.hide();
17659                 break;
17660             case 37: // left
17661             case 39: // right
17662                 this.onTogglePeriod();
17663                 break;
17664             case 38: // up
17665                 this.onIncrementMinutes();
17666                 break;
17667             case 40: // down
17668                 this.onDecrementMinutes();
17669                 break;
17670             case 13: // enter
17671             case 9: // tab
17672                 this.setTime();
17673                 break;
17674         }
17675     },
17676     
17677     onClick: function(e) {
17678         e.stopPropagation();
17679         e.preventDefault();
17680     },
17681     
17682     picker : function()
17683     {
17684         return this.el.select('.datepicker', true).first();
17685     },
17686     
17687     fillTime: function()
17688     {    
17689         var time = this.pop.select('tbody', true).first();
17690         
17691         time.dom.innerHTML = '';
17692         
17693         time.createChild({
17694             tag: 'tr',
17695             cn: [
17696                 {
17697                     tag: 'td',
17698                     cn: [
17699                         {
17700                             tag: 'a',
17701                             href: '#',
17702                             cls: 'btn',
17703                             cn: [
17704                                 {
17705                                     tag: 'span',
17706                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17707                                 }
17708                             ]
17709                         } 
17710                     ]
17711                 },
17712                 {
17713                     tag: 'td',
17714                     cls: 'separator'
17715                 },
17716                 {
17717                     tag: 'td',
17718                     cn: [
17719                         {
17720                             tag: 'a',
17721                             href: '#',
17722                             cls: 'btn',
17723                             cn: [
17724                                 {
17725                                     tag: 'span',
17726                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17727                                 }
17728                             ]
17729                         }
17730                     ]
17731                 },
17732                 {
17733                     tag: 'td',
17734                     cls: 'separator'
17735                 }
17736             ]
17737         });
17738         
17739         time.createChild({
17740             tag: 'tr',
17741             cn: [
17742                 {
17743                     tag: 'td',
17744                     cn: [
17745                         {
17746                             tag: 'span',
17747                             cls: 'timepicker-hour',
17748                             html: '00'
17749                         }  
17750                     ]
17751                 },
17752                 {
17753                     tag: 'td',
17754                     cls: 'separator',
17755                     html: ':'
17756                 },
17757                 {
17758                     tag: 'td',
17759                     cn: [
17760                         {
17761                             tag: 'span',
17762                             cls: 'timepicker-minute',
17763                             html: '00'
17764                         }  
17765                     ]
17766                 },
17767                 {
17768                     tag: 'td',
17769                     cls: 'separator'
17770                 },
17771                 {
17772                     tag: 'td',
17773                     cn: [
17774                         {
17775                             tag: 'button',
17776                             type: 'button',
17777                             cls: 'btn btn-primary period',
17778                             html: 'AM'
17779                             
17780                         }
17781                     ]
17782                 }
17783             ]
17784         });
17785         
17786         time.createChild({
17787             tag: 'tr',
17788             cn: [
17789                 {
17790                     tag: 'td',
17791                     cn: [
17792                         {
17793                             tag: 'a',
17794                             href: '#',
17795                             cls: 'btn',
17796                             cn: [
17797                                 {
17798                                     tag: 'span',
17799                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17800                                 }
17801                             ]
17802                         }
17803                     ]
17804                 },
17805                 {
17806                     tag: 'td',
17807                     cls: 'separator'
17808                 },
17809                 {
17810                     tag: 'td',
17811                     cn: [
17812                         {
17813                             tag: 'a',
17814                             href: '#',
17815                             cls: 'btn',
17816                             cn: [
17817                                 {
17818                                     tag: 'span',
17819                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17820                                 }
17821                             ]
17822                         }
17823                     ]
17824                 },
17825                 {
17826                     tag: 'td',
17827                     cls: 'separator'
17828                 }
17829             ]
17830         });
17831         
17832     },
17833     
17834     update: function()
17835     {
17836         
17837         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17838         
17839         this.fill();
17840     },
17841     
17842     fill: function() 
17843     {
17844         var hours = this.time.getHours();
17845         var minutes = this.time.getMinutes();
17846         var period = 'AM';
17847         
17848         if(hours > 11){
17849             period = 'PM';
17850         }
17851         
17852         if(hours == 0){
17853             hours = 12;
17854         }
17855         
17856         
17857         if(hours > 12){
17858             hours = hours - 12;
17859         }
17860         
17861         if(hours < 10){
17862             hours = '0' + hours;
17863         }
17864         
17865         if(minutes < 10){
17866             minutes = '0' + minutes;
17867         }
17868         
17869         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17870         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17871         this.pop.select('button', true).first().dom.innerHTML = period;
17872         
17873     },
17874     
17875     place: function()
17876     {   
17877         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17878         
17879         var cls = ['bottom'];
17880         
17881         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17882             cls.pop();
17883             cls.push('top');
17884         }
17885         
17886         cls.push('right');
17887         
17888         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17889             cls.pop();
17890             cls.push('left');
17891         }
17892         
17893         this.picker().addClass(cls.join('-'));
17894         
17895         var _this = this;
17896         
17897         Roo.each(cls, function(c){
17898             if(c == 'bottom'){
17899                 _this.picker().setTop(_this.inputEl().getHeight());
17900                 return;
17901             }
17902             if(c == 'top'){
17903                 _this.picker().setTop(0 - _this.picker().getHeight());
17904                 return;
17905             }
17906             
17907             if(c == 'left'){
17908                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17909                 return;
17910             }
17911             if(c == 'right'){
17912                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17913                 return;
17914             }
17915         });
17916         
17917     },
17918   
17919     onFocus : function()
17920     {
17921         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17922         this.show();
17923     },
17924     
17925     onBlur : function()
17926     {
17927         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17928         this.hide();
17929     },
17930     
17931     show : function()
17932     {
17933         this.picker().show();
17934         this.pop.show();
17935         this.update();
17936         this.place();
17937         
17938         this.fireEvent('show', this, this.date);
17939     },
17940     
17941     hide : function()
17942     {
17943         this.picker().hide();
17944         this.pop.hide();
17945         
17946         this.fireEvent('hide', this, this.date);
17947     },
17948     
17949     setTime : function()
17950     {
17951         this.hide();
17952         this.setValue(this.time.format(this.format));
17953         
17954         this.fireEvent('select', this, this.date);
17955         
17956         
17957     },
17958     
17959     onMousedown: function(e){
17960         e.stopPropagation();
17961         e.preventDefault();
17962     },
17963     
17964     onIncrementHours: function()
17965     {
17966         Roo.log('onIncrementHours');
17967         this.time = this.time.add(Date.HOUR, 1);
17968         this.update();
17969         
17970     },
17971     
17972     onDecrementHours: function()
17973     {
17974         Roo.log('onDecrementHours');
17975         this.time = this.time.add(Date.HOUR, -1);
17976         this.update();
17977     },
17978     
17979     onIncrementMinutes: function()
17980     {
17981         Roo.log('onIncrementMinutes');
17982         this.time = this.time.add(Date.MINUTE, 1);
17983         this.update();
17984     },
17985     
17986     onDecrementMinutes: function()
17987     {
17988         Roo.log('onDecrementMinutes');
17989         this.time = this.time.add(Date.MINUTE, -1);
17990         this.update();
17991     },
17992     
17993     onTogglePeriod: function()
17994     {
17995         Roo.log('onTogglePeriod');
17996         this.time = this.time.add(Date.HOUR, 12);
17997         this.update();
17998     }
17999     
18000    
18001 });
18002
18003 Roo.apply(Roo.bootstrap.TimeField,  {
18004     
18005     content : {
18006         tag: 'tbody',
18007         cn: [
18008             {
18009                 tag: 'tr',
18010                 cn: [
18011                 {
18012                     tag: 'td',
18013                     colspan: '7'
18014                 }
18015                 ]
18016             }
18017         ]
18018     },
18019     
18020     footer : {
18021         tag: 'tfoot',
18022         cn: [
18023             {
18024                 tag: 'tr',
18025                 cn: [
18026                 {
18027                     tag: 'th',
18028                     colspan: '7',
18029                     cls: '',
18030                     cn: [
18031                         {
18032                             tag: 'button',
18033                             cls: 'btn btn-info ok',
18034                             html: 'OK'
18035                         }
18036                     ]
18037                 }
18038
18039                 ]
18040             }
18041         ]
18042     }
18043 });
18044
18045 Roo.apply(Roo.bootstrap.TimeField,  {
18046   
18047     template : {
18048         tag: 'div',
18049         cls: 'datepicker dropdown-menu',
18050         cn: [
18051             {
18052                 tag: 'div',
18053                 cls: 'datepicker-time',
18054                 cn: [
18055                 {
18056                     tag: 'table',
18057                     cls: 'table-condensed',
18058                     cn:[
18059                     Roo.bootstrap.TimeField.content,
18060                     Roo.bootstrap.TimeField.footer
18061                     ]
18062                 }
18063                 ]
18064             }
18065         ]
18066     }
18067 });
18068
18069  
18070
18071  /*
18072  * - LGPL
18073  *
18074  * MonthField
18075  * 
18076  */
18077
18078 /**
18079  * @class Roo.bootstrap.MonthField
18080  * @extends Roo.bootstrap.Input
18081  * Bootstrap MonthField class
18082  * 
18083  * @cfg {String} language default en
18084  * 
18085  * @constructor
18086  * Create a new MonthField
18087  * @param {Object} config The config object
18088  */
18089
18090 Roo.bootstrap.MonthField = function(config){
18091     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18092     
18093     this.addEvents({
18094         /**
18095          * @event show
18096          * Fires when this field show.
18097          * @param {Roo.bootstrap.MonthField} this
18098          * @param {Mixed} date The date value
18099          */
18100         show : true,
18101         /**
18102          * @event show
18103          * Fires when this field hide.
18104          * @param {Roo.bootstrap.MonthField} this
18105          * @param {Mixed} date The date value
18106          */
18107         hide : true,
18108         /**
18109          * @event select
18110          * Fires when select a date.
18111          * @param {Roo.bootstrap.MonthField} this
18112          * @param {String} oldvalue The old value
18113          * @param {String} newvalue The new value
18114          */
18115         select : true
18116     });
18117 };
18118
18119 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18120     
18121     onRender: function(ct, position)
18122     {
18123         
18124         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18125         
18126         this.language = this.language || 'en';
18127         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18128         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18129         
18130         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18131         this.isInline = false;
18132         this.isInput = true;
18133         this.component = this.el.select('.add-on', true).first() || false;
18134         this.component = (this.component && this.component.length === 0) ? false : this.component;
18135         this.hasInput = this.component && this.inputEL().length;
18136         
18137         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18138         
18139         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18140         
18141         this.picker().on('mousedown', this.onMousedown, this);
18142         this.picker().on('click', this.onClick, this);
18143         
18144         this.picker().addClass('datepicker-dropdown');
18145         
18146         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18147             v.setStyle('width', '189px');
18148         });
18149         
18150         this.fillMonths();
18151         
18152         this.update();
18153         
18154         if(this.isInline) {
18155             this.show();
18156         }
18157         
18158     },
18159     
18160     setValue: function(v, suppressEvent)
18161     {   
18162         var o = this.getValue();
18163         
18164         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18165         
18166         this.update();
18167
18168         if(suppressEvent !== true){
18169             this.fireEvent('select', this, o, v);
18170         }
18171         
18172     },
18173     
18174     getValue: function()
18175     {
18176         return this.value;
18177     },
18178     
18179     onClick: function(e) 
18180     {
18181         e.stopPropagation();
18182         e.preventDefault();
18183         
18184         var target = e.getTarget();
18185         
18186         if(target.nodeName.toLowerCase() === 'i'){
18187             target = Roo.get(target).dom.parentNode;
18188         }
18189         
18190         var nodeName = target.nodeName;
18191         var className = target.className;
18192         var html = target.innerHTML;
18193         
18194         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18195             return;
18196         }
18197         
18198         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18199         
18200         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18201         
18202         this.hide();
18203                         
18204     },
18205     
18206     picker : function()
18207     {
18208         return this.pickerEl;
18209     },
18210     
18211     fillMonths: function()
18212     {    
18213         var i = 0;
18214         var months = this.picker().select('>.datepicker-months td', true).first();
18215         
18216         months.dom.innerHTML = '';
18217         
18218         while (i < 12) {
18219             var month = {
18220                 tag: 'span',
18221                 cls: 'month',
18222                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18223             };
18224             
18225             months.createChild(month);
18226         }
18227         
18228     },
18229     
18230     update: function()
18231     {
18232         var _this = this;
18233         
18234         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18235             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18236         }
18237         
18238         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18239             e.removeClass('active');
18240             
18241             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18242                 e.addClass('active');
18243             }
18244         })
18245     },
18246     
18247     place: function()
18248     {
18249         if(this.isInline) {
18250             return;
18251         }
18252         
18253         this.picker().removeClass(['bottom', 'top']);
18254         
18255         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18256             /*
18257              * place to the top of element!
18258              *
18259              */
18260             
18261             this.picker().addClass('top');
18262             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18263             
18264             return;
18265         }
18266         
18267         this.picker().addClass('bottom');
18268         
18269         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18270     },
18271     
18272     onFocus : function()
18273     {
18274         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18275         this.show();
18276     },
18277     
18278     onBlur : function()
18279     {
18280         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18281         
18282         var d = this.inputEl().getValue();
18283         
18284         this.setValue(d);
18285                 
18286         this.hide();
18287     },
18288     
18289     show : function()
18290     {
18291         this.picker().show();
18292         this.picker().select('>.datepicker-months', true).first().show();
18293         this.update();
18294         this.place();
18295         
18296         this.fireEvent('show', this, this.date);
18297     },
18298     
18299     hide : function()
18300     {
18301         if(this.isInline) {
18302             return;
18303         }
18304         this.picker().hide();
18305         this.fireEvent('hide', this, this.date);
18306         
18307     },
18308     
18309     onMousedown: function(e)
18310     {
18311         e.stopPropagation();
18312         e.preventDefault();
18313     },
18314     
18315     keyup: function(e)
18316     {
18317         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18318         this.update();
18319     },
18320
18321     fireKey: function(e)
18322     {
18323         if (!this.picker().isVisible()){
18324             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18325                 this.show();
18326             }
18327             return;
18328         }
18329         
18330         var dir;
18331         
18332         switch(e.keyCode){
18333             case 27: // escape
18334                 this.hide();
18335                 e.preventDefault();
18336                 break;
18337             case 37: // left
18338             case 39: // right
18339                 dir = e.keyCode == 37 ? -1 : 1;
18340                 
18341                 this.vIndex = this.vIndex + dir;
18342                 
18343                 if(this.vIndex < 0){
18344                     this.vIndex = 0;
18345                 }
18346                 
18347                 if(this.vIndex > 11){
18348                     this.vIndex = 11;
18349                 }
18350                 
18351                 if(isNaN(this.vIndex)){
18352                     this.vIndex = 0;
18353                 }
18354                 
18355                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18356                 
18357                 break;
18358             case 38: // up
18359             case 40: // down
18360                 
18361                 dir = e.keyCode == 38 ? -1 : 1;
18362                 
18363                 this.vIndex = this.vIndex + dir * 4;
18364                 
18365                 if(this.vIndex < 0){
18366                     this.vIndex = 0;
18367                 }
18368                 
18369                 if(this.vIndex > 11){
18370                     this.vIndex = 11;
18371                 }
18372                 
18373                 if(isNaN(this.vIndex)){
18374                     this.vIndex = 0;
18375                 }
18376                 
18377                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18378                 break;
18379                 
18380             case 13: // enter
18381                 
18382                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18383                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18384                 }
18385                 
18386                 this.hide();
18387                 e.preventDefault();
18388                 break;
18389             case 9: // tab
18390                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18391                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18392                 }
18393                 this.hide();
18394                 break;
18395             case 16: // shift
18396             case 17: // ctrl
18397             case 18: // alt
18398                 break;
18399             default :
18400                 this.hide();
18401                 
18402         }
18403     },
18404     
18405     remove: function() 
18406     {
18407         this.picker().remove();
18408     }
18409    
18410 });
18411
18412 Roo.apply(Roo.bootstrap.MonthField,  {
18413     
18414     content : {
18415         tag: 'tbody',
18416         cn: [
18417         {
18418             tag: 'tr',
18419             cn: [
18420             {
18421                 tag: 'td',
18422                 colspan: '7'
18423             }
18424             ]
18425         }
18426         ]
18427     },
18428     
18429     dates:{
18430         en: {
18431             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18432             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18433         }
18434     }
18435 });
18436
18437 Roo.apply(Roo.bootstrap.MonthField,  {
18438   
18439     template : {
18440         tag: 'div',
18441         cls: 'datepicker dropdown-menu roo-dynamic',
18442         cn: [
18443             {
18444                 tag: 'div',
18445                 cls: 'datepicker-months',
18446                 cn: [
18447                 {
18448                     tag: 'table',
18449                     cls: 'table-condensed',
18450                     cn:[
18451                         Roo.bootstrap.DateField.content
18452                     ]
18453                 }
18454                 ]
18455             }
18456         ]
18457     }
18458 });
18459
18460  
18461
18462  
18463  /*
18464  * - LGPL
18465  *
18466  * CheckBox
18467  * 
18468  */
18469
18470 /**
18471  * @class Roo.bootstrap.CheckBox
18472  * @extends Roo.bootstrap.Input
18473  * Bootstrap CheckBox class
18474  * 
18475  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18476  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18477  * @cfg {String} boxLabel The text that appears beside the checkbox
18478  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18479  * @cfg {Boolean} checked initnal the element
18480  * @cfg {Boolean} inline inline the element (default false)
18481  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18482  * 
18483  * @constructor
18484  * Create a new CheckBox
18485  * @param {Object} config The config object
18486  */
18487
18488 Roo.bootstrap.CheckBox = function(config){
18489     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18490    
18491     this.addEvents({
18492         /**
18493         * @event check
18494         * Fires when the element is checked or unchecked.
18495         * @param {Roo.bootstrap.CheckBox} this This input
18496         * @param {Boolean} checked The new checked value
18497         */
18498        check : true
18499     });
18500     
18501 };
18502
18503 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18504   
18505     inputType: 'checkbox',
18506     inputValue: 1,
18507     valueOff: 0,
18508     boxLabel: false,
18509     checked: false,
18510     weight : false,
18511     inline: false,
18512     
18513     getAutoCreate : function()
18514     {
18515         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18516         
18517         var id = Roo.id();
18518         
18519         var cfg = {};
18520         
18521         cfg.cls = 'form-group ' + this.inputType; //input-group
18522         
18523         if(this.inline){
18524             cfg.cls += ' ' + this.inputType + '-inline';
18525         }
18526         
18527         var input =  {
18528             tag: 'input',
18529             id : id,
18530             type : this.inputType,
18531             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18532             cls : 'roo-' + this.inputType, //'form-box',
18533             placeholder : this.placeholder || ''
18534             
18535         };
18536         
18537         if (this.weight) { // Validity check?
18538             cfg.cls += " " + this.inputType + "-" + this.weight;
18539         }
18540         
18541         if (this.disabled) {
18542             input.disabled=true;
18543         }
18544         
18545         if(this.checked){
18546             input.checked = this.checked;
18547         }
18548         
18549         if (this.name) {
18550             input.name = this.name;
18551         }
18552         
18553         if (this.size) {
18554             input.cls += ' input-' + this.size;
18555         }
18556         
18557         var settings=this;
18558         
18559         ['xs','sm','md','lg'].map(function(size){
18560             if (settings[size]) {
18561                 cfg.cls += ' col-' + size + '-' + settings[size];
18562             }
18563         });
18564         
18565         var inputblock = input;
18566          
18567         if (this.before || this.after) {
18568             
18569             inputblock = {
18570                 cls : 'input-group',
18571                 cn :  [] 
18572             };
18573             
18574             if (this.before) {
18575                 inputblock.cn.push({
18576                     tag :'span',
18577                     cls : 'input-group-addon',
18578                     html : this.before
18579                 });
18580             }
18581             
18582             inputblock.cn.push(input);
18583             
18584             if (this.after) {
18585                 inputblock.cn.push({
18586                     tag :'span',
18587                     cls : 'input-group-addon',
18588                     html : this.after
18589                 });
18590             }
18591             
18592         }
18593         
18594         if (align ==='left' && this.fieldLabel.length) {
18595                 Roo.log("left and has label");
18596                 cfg.cn = [
18597                     
18598                     {
18599                         tag: 'label',
18600                         'for' :  id,
18601                         cls : 'control-label col-md-' + this.labelWidth,
18602                         html : this.fieldLabel
18603                         
18604                     },
18605                     {
18606                         cls : "col-md-" + (12 - this.labelWidth), 
18607                         cn: [
18608                             inputblock
18609                         ]
18610                     }
18611                     
18612                 ];
18613         } else if ( this.fieldLabel.length) {
18614                 Roo.log(" label");
18615                 cfg.cn = [
18616                    
18617                     {
18618                         tag: this.boxLabel ? 'span' : 'label',
18619                         'for': id,
18620                         cls: 'control-label box-input-label',
18621                         //cls : 'input-group-addon',
18622                         html : this.fieldLabel
18623                         
18624                     },
18625                     
18626                     inputblock
18627                     
18628                 ];
18629
18630         } else {
18631             
18632                 Roo.log(" no label && no align");
18633                 cfg.cn = [  inputblock ] ;
18634                 
18635                 
18636         }
18637         if(this.boxLabel){
18638              var boxLabelCfg = {
18639                 tag: 'label',
18640                 //'for': id, // box label is handled by onclick - so no for...
18641                 cls: 'box-label',
18642                 html: this.boxLabel
18643             };
18644             
18645             if(this.tooltip){
18646                 boxLabelCfg.tooltip = this.tooltip;
18647             }
18648              
18649             cfg.cn.push(boxLabelCfg);
18650         }
18651         
18652         
18653        
18654         return cfg;
18655         
18656     },
18657     
18658     /**
18659      * return the real input element.
18660      */
18661     inputEl: function ()
18662     {
18663         return this.el.select('input.roo-' + this.inputType,true).first();
18664     },
18665     
18666     labelEl: function()
18667     {
18668         return this.el.select('label.control-label',true).first();
18669     },
18670     /* depricated... */
18671     
18672     label: function()
18673     {
18674         return this.labelEl();
18675     },
18676     
18677     initEvents : function()
18678     {
18679 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18680         
18681         this.inputEl().on('click', this.onClick,  this);
18682         
18683         if (this.boxLabel) { 
18684             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18685         }
18686         
18687         this.startValue = this.getValue();
18688         
18689         if(this.groupId){
18690             Roo.bootstrap.CheckBox.register(this);
18691         }
18692     },
18693     
18694     onClick : function()
18695     {   
18696         this.setChecked(!this.checked);
18697     },
18698     
18699     setChecked : function(state,suppressEvent)
18700     {
18701         this.startValue = this.getValue();
18702         
18703         if(this.inputType == 'radio'){
18704             
18705             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18706                 e.dom.checked = false;
18707             });
18708             
18709             this.inputEl().dom.checked = true;
18710             
18711             this.inputEl().dom.value = this.inputValue;
18712             
18713             if(suppressEvent !== true){
18714                 this.fireEvent('check', this, true);
18715             }
18716             
18717             this.validate();
18718             
18719             return;
18720         }
18721         
18722         this.checked = state;
18723         
18724         this.inputEl().dom.checked = state;
18725         
18726         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18727         
18728         if(suppressEvent !== true){
18729             this.fireEvent('check', this, state);
18730         }
18731         
18732         this.validate();
18733     },
18734     
18735     getValue : function()
18736     {
18737         if(this.inputType == 'radio'){
18738             return this.getGroupValue();
18739         }
18740         
18741         return this.inputEl().getValue();
18742         
18743     },
18744     
18745     getGroupValue : function()
18746     {
18747         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18748             return '';
18749         }
18750         
18751         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18752     },
18753     
18754     setValue : function(v,suppressEvent)
18755     {
18756         if(this.inputType == 'radio'){
18757             this.setGroupValue(v, suppressEvent);
18758             return;
18759         }
18760         
18761         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18762         
18763         this.validate();
18764     },
18765     
18766     setGroupValue : function(v, suppressEvent)
18767     {
18768         this.startValue = this.getValue();
18769         
18770         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18771             e.dom.checked = false;
18772             
18773             if(e.dom.value == v){
18774                 e.dom.checked = true;
18775             }
18776         });
18777         
18778         if(suppressEvent !== true){
18779             this.fireEvent('check', this, true);
18780         }
18781
18782         this.validate();
18783         
18784         return;
18785     },
18786     
18787     validate : function()
18788     {
18789         if(
18790                 this.disabled || 
18791                 (this.inputType == 'radio' && this.validateRadio()) ||
18792                 (this.inputType == 'checkbox' && this.validateCheckbox())
18793         ){
18794             this.markValid();
18795             return true;
18796         }
18797         
18798         this.markInvalid();
18799         return false;
18800     },
18801     
18802     validateRadio : function()
18803     {
18804         var valid = false;
18805         
18806         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18807             if(!e.dom.checked){
18808                 return;
18809             }
18810             
18811             valid = true;
18812             
18813             return false;
18814         });
18815         
18816         return valid;
18817     },
18818     
18819     validateCheckbox : function()
18820     {
18821         if(!this.groupId){
18822             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18823         }
18824         
18825         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18826         
18827         if(!group){
18828             return false;
18829         }
18830         
18831         var r = false;
18832         
18833         for(var i in group){
18834             if(r){
18835                 break;
18836             }
18837             
18838             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18839         }
18840         
18841         return r;
18842     },
18843     
18844     /**
18845      * Mark this field as valid
18846      */
18847     markValid : function()
18848     {
18849         if(this.allowBlank){
18850             return;
18851         }
18852         
18853         var _this = this;
18854         
18855         this.fireEvent('valid', this);
18856         
18857         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18858         
18859         if(this.groupId){
18860             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18861         }
18862         
18863         if(label){
18864             label.markValid();
18865         }
18866         
18867         if(this.inputType == 'radio'){
18868             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18869                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18870                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18871             });
18872             
18873             return;
18874         }
18875         
18876         if(!this.groupId){
18877             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18878             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18879             return;
18880         }
18881         
18882         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18883             
18884         if(!group){
18885             return;
18886         }
18887         
18888         for(var i in group){
18889             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18890             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18891         }
18892     },
18893     
18894      /**
18895      * Mark this field as invalid
18896      * @param {String} msg The validation message
18897      */
18898     markInvalid : function(msg)
18899     {
18900         if(this.allowBlank){
18901             return;
18902         }
18903         
18904         var _this = this;
18905         
18906         this.fireEvent('invalid', this, msg);
18907         
18908         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18909         
18910         if(this.groupId){
18911             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18912         }
18913         
18914         if(label){
18915             label.markInvalid();
18916         }
18917             
18918         if(this.inputType == 'radio'){
18919             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18920                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18921                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18922             });
18923             
18924             return;
18925         }
18926         
18927         if(!this.groupId){
18928             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18929             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18930             return;
18931         }
18932         
18933         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18934         
18935         if(!group){
18936             return;
18937         }
18938         
18939         for(var i in group){
18940             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18941             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18942         }
18943         
18944     }
18945     
18946 });
18947
18948 Roo.apply(Roo.bootstrap.CheckBox, {
18949     
18950     groups: {},
18951     
18952      /**
18953     * register a CheckBox Group
18954     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18955     */
18956     register : function(checkbox)
18957     {
18958         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18959             this.groups[checkbox.groupId] = {};
18960         }
18961         
18962         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18963             return;
18964         }
18965         
18966         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18967         
18968     },
18969     /**
18970     * fetch a CheckBox Group based on the group ID
18971     * @param {string} the group ID
18972     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18973     */
18974     get: function(groupId) {
18975         if (typeof(this.groups[groupId]) == 'undefined') {
18976             return false;
18977         }
18978         
18979         return this.groups[groupId] ;
18980     }
18981     
18982     
18983 });
18984 /*
18985  * - LGPL
18986  *
18987  * Radio
18988  *
18989  *
18990  * not inline
18991  *<div class="radio">
18992   <label>
18993     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18994     Option one is this and that&mdash;be sure to include why it's great
18995   </label>
18996 </div>
18997  *
18998  *
18999  *inline
19000  *<span>
19001  *<label class="radio-inline">fieldLabel</label>
19002  *<label class="radio-inline">
19003   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19004 </label>
19005 <span>
19006  * 
19007  * 
19008  */
19009
19010 /**
19011  * @class Roo.bootstrap.Radio
19012  * @extends Roo.bootstrap.CheckBox
19013  * Bootstrap Radio class
19014
19015  * @constructor
19016  * Create a new Radio
19017  * @param {Object} config The config object
19018  */
19019
19020 Roo.bootstrap.Radio = function(config){
19021     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19022    
19023 };
19024
19025 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19026     
19027     inputType: 'radio',
19028     inputValue: '',
19029     valueOff: '',
19030     
19031     getAutoCreate : function()
19032     {
19033         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19034         align = align || 'left'; // default...
19035         
19036         
19037         
19038         var id = Roo.id();
19039         
19040         var cfg = {
19041                 tag : this.inline ? 'span' : 'div',
19042                 cls : '',
19043                 cn : []
19044         };
19045         
19046         var inline = this.inline ? ' radio-inline' : '';
19047         
19048         var lbl = {
19049                 tag: 'label' ,
19050                 // does not need for, as we wrap the input with it..
19051                 'for' : id,
19052                 cls : 'control-label box-label' + inline,
19053                 cn : []
19054         };
19055         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19056         
19057         var fieldLabel = {
19058             tag: 'label' ,
19059             //cls : 'control-label' + inline,
19060             html : this.fieldLabel,
19061             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19062         };
19063         
19064  
19065         
19066         
19067         var input =  {
19068             tag: 'input',
19069             id : id,
19070             type : this.inputType,
19071             //value : (!this.checked) ? this.valueOff : this.inputValue,
19072             value : this.inputValue,
19073             cls : 'roo-radio',
19074             placeholder : this.placeholder || '' // ?? needed????
19075             
19076         };
19077         if (this.weight) { // Validity check?
19078             input.cls += " radio-" + this.weight;
19079         }
19080         if (this.disabled) {
19081             input.disabled=true;
19082         }
19083         
19084         if(this.checked){
19085             input.checked = this.checked;
19086         }
19087         
19088         if (this.name) {
19089             input.name = this.name;
19090         }
19091         
19092         if (this.size) {
19093             input.cls += ' input-' + this.size;
19094         }
19095         
19096         //?? can span's inline have a width??
19097         
19098         var settings=this;
19099         ['xs','sm','md','lg'].map(function(size){
19100             if (settings[size]) {
19101                 cfg.cls += ' col-' + size + '-' + settings[size];
19102             }
19103         });
19104         
19105         var inputblock = input;
19106         
19107         if (this.before || this.after) {
19108             
19109             inputblock = {
19110                 cls : 'input-group',
19111                 tag : 'span',
19112                 cn :  [] 
19113             };
19114             if (this.before) {
19115                 inputblock.cn.push({
19116                     tag :'span',
19117                     cls : 'input-group-addon',
19118                     html : this.before
19119                 });
19120             }
19121             inputblock.cn.push(input);
19122             if (this.after) {
19123                 inputblock.cn.push({
19124                     tag :'span',
19125                     cls : 'input-group-addon',
19126                     html : this.after
19127                 });
19128             }
19129             
19130         };
19131         
19132         
19133         if (this.fieldLabel && this.fieldLabel.length) {
19134             cfg.cn.push(fieldLabel);
19135         }
19136        
19137         // normal bootstrap puts the input inside the label.
19138         // however with our styled version - it has to go after the input.
19139        
19140         //lbl.cn.push(inputblock);
19141         
19142         var lblwrap =  {
19143             tag: 'span',
19144             cls: 'radio' + inline,
19145             cn: [
19146                 inputblock,
19147                 lbl
19148             ]
19149         };
19150         
19151         cfg.cn.push( lblwrap);
19152         
19153         if(this.boxLabel){
19154             lbl.cn.push({
19155                 tag: 'span',
19156                 html: this.boxLabel
19157             })
19158         }
19159          
19160         
19161         return cfg;
19162         
19163     },
19164     
19165     initEvents : function()
19166     {
19167 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19168         
19169         this.inputEl().on('click', this.onClick,  this);
19170         if (this.boxLabel) {
19171             //Roo.log('find label');
19172             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19173         }
19174         
19175     },
19176     
19177     inputEl: function ()
19178     {
19179         return this.el.select('input.roo-radio',true).first();
19180     },
19181     onClick : function()
19182     {   
19183         Roo.log("click");
19184         this.setChecked(true);
19185     },
19186     
19187     setChecked : function(state,suppressEvent)
19188     {
19189         if(state){
19190             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19191                 v.dom.checked = false;
19192             });
19193         }
19194         Roo.log(this.inputEl().dom);
19195         this.checked = state;
19196         this.inputEl().dom.checked = state;
19197         
19198         if(suppressEvent !== true){
19199             this.fireEvent('check', this, state);
19200         }
19201         
19202         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19203         
19204     },
19205     
19206     getGroupValue : function()
19207     {
19208         var value = '';
19209         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19210             if(v.dom.checked == true){
19211                 value = v.dom.value;
19212             }
19213         });
19214         
19215         return value;
19216     },
19217     
19218     /**
19219      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19220      * @return {Mixed} value The field value
19221      */
19222     getValue : function(){
19223         return this.getGroupValue();
19224     }
19225     
19226 });
19227
19228  
19229 //<script type="text/javascript">
19230
19231 /*
19232  * Based  Ext JS Library 1.1.1
19233  * Copyright(c) 2006-2007, Ext JS, LLC.
19234  * LGPL
19235  *
19236  */
19237  
19238 /**
19239  * @class Roo.HtmlEditorCore
19240  * @extends Roo.Component
19241  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19242  *
19243  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19244  */
19245
19246 Roo.HtmlEditorCore = function(config){
19247     
19248     
19249     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19250     
19251     
19252     this.addEvents({
19253         /**
19254          * @event initialize
19255          * Fires when the editor is fully initialized (including the iframe)
19256          * @param {Roo.HtmlEditorCore} this
19257          */
19258         initialize: true,
19259         /**
19260          * @event activate
19261          * Fires when the editor is first receives the focus. Any insertion must wait
19262          * until after this event.
19263          * @param {Roo.HtmlEditorCore} this
19264          */
19265         activate: true,
19266          /**
19267          * @event beforesync
19268          * Fires before the textarea is updated with content from the editor iframe. Return false
19269          * to cancel the sync.
19270          * @param {Roo.HtmlEditorCore} this
19271          * @param {String} html
19272          */
19273         beforesync: true,
19274          /**
19275          * @event beforepush
19276          * Fires before the iframe editor is updated with content from the textarea. Return false
19277          * to cancel the push.
19278          * @param {Roo.HtmlEditorCore} this
19279          * @param {String} html
19280          */
19281         beforepush: true,
19282          /**
19283          * @event sync
19284          * Fires when the textarea is updated with content from the editor iframe.
19285          * @param {Roo.HtmlEditorCore} this
19286          * @param {String} html
19287          */
19288         sync: true,
19289          /**
19290          * @event push
19291          * Fires when the iframe editor is updated with content from the textarea.
19292          * @param {Roo.HtmlEditorCore} this
19293          * @param {String} html
19294          */
19295         push: true,
19296         
19297         /**
19298          * @event editorevent
19299          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19300          * @param {Roo.HtmlEditorCore} this
19301          */
19302         editorevent: true
19303         
19304     });
19305     
19306     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19307     
19308     // defaults : white / black...
19309     this.applyBlacklists();
19310     
19311     
19312     
19313 };
19314
19315
19316 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19317
19318
19319      /**
19320      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19321      */
19322     
19323     owner : false,
19324     
19325      /**
19326      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19327      *                        Roo.resizable.
19328      */
19329     resizable : false,
19330      /**
19331      * @cfg {Number} height (in pixels)
19332      */   
19333     height: 300,
19334    /**
19335      * @cfg {Number} width (in pixels)
19336      */   
19337     width: 500,
19338     
19339     /**
19340      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19341      * 
19342      */
19343     stylesheets: false,
19344     
19345     // id of frame..
19346     frameId: false,
19347     
19348     // private properties
19349     validationEvent : false,
19350     deferHeight: true,
19351     initialized : false,
19352     activated : false,
19353     sourceEditMode : false,
19354     onFocus : Roo.emptyFn,
19355     iframePad:3,
19356     hideMode:'offsets',
19357     
19358     clearUp: true,
19359     
19360     // blacklist + whitelisted elements..
19361     black: false,
19362     white: false,
19363      
19364     
19365
19366     /**
19367      * Protected method that will not generally be called directly. It
19368      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19369      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19370      */
19371     getDocMarkup : function(){
19372         // body styles..
19373         var st = '';
19374         
19375         // inherit styels from page...?? 
19376         if (this.stylesheets === false) {
19377             
19378             Roo.get(document.head).select('style').each(function(node) {
19379                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19380             });
19381             
19382             Roo.get(document.head).select('link').each(function(node) { 
19383                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19384             });
19385             
19386         } else if (!this.stylesheets.length) {
19387                 // simple..
19388                 st = '<style type="text/css">' +
19389                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19390                    '</style>';
19391         } else { 
19392             
19393         }
19394         
19395         st +=  '<style type="text/css">' +
19396             'IMG { cursor: pointer } ' +
19397         '</style>';
19398
19399         
19400         return '<html><head>' + st  +
19401             //<style type="text/css">' +
19402             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19403             //'</style>' +
19404             ' </head><body class="roo-htmleditor-body"></body></html>';
19405     },
19406
19407     // private
19408     onRender : function(ct, position)
19409     {
19410         var _t = this;
19411         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19412         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19413         
19414         
19415         this.el.dom.style.border = '0 none';
19416         this.el.dom.setAttribute('tabIndex', -1);
19417         this.el.addClass('x-hidden hide');
19418         
19419         
19420         
19421         if(Roo.isIE){ // fix IE 1px bogus margin
19422             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19423         }
19424        
19425         
19426         this.frameId = Roo.id();
19427         
19428          
19429         
19430         var iframe = this.owner.wrap.createChild({
19431             tag: 'iframe',
19432             cls: 'form-control', // bootstrap..
19433             id: this.frameId,
19434             name: this.frameId,
19435             frameBorder : 'no',
19436             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19437         }, this.el
19438         );
19439         
19440         
19441         this.iframe = iframe.dom;
19442
19443          this.assignDocWin();
19444         
19445         this.doc.designMode = 'on';
19446        
19447         this.doc.open();
19448         this.doc.write(this.getDocMarkup());
19449         this.doc.close();
19450
19451         
19452         var task = { // must defer to wait for browser to be ready
19453             run : function(){
19454                 //console.log("run task?" + this.doc.readyState);
19455                 this.assignDocWin();
19456                 if(this.doc.body || this.doc.readyState == 'complete'){
19457                     try {
19458                         this.doc.designMode="on";
19459                     } catch (e) {
19460                         return;
19461                     }
19462                     Roo.TaskMgr.stop(task);
19463                     this.initEditor.defer(10, this);
19464                 }
19465             },
19466             interval : 10,
19467             duration: 10000,
19468             scope: this
19469         };
19470         Roo.TaskMgr.start(task);
19471
19472     },
19473
19474     // private
19475     onResize : function(w, h)
19476     {
19477          Roo.log('resize: ' +w + ',' + h );
19478         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19479         if(!this.iframe){
19480             return;
19481         }
19482         if(typeof w == 'number'){
19483             
19484             this.iframe.style.width = w + 'px';
19485         }
19486         if(typeof h == 'number'){
19487             
19488             this.iframe.style.height = h + 'px';
19489             if(this.doc){
19490                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19491             }
19492         }
19493         
19494     },
19495
19496     /**
19497      * Toggles the editor between standard and source edit mode.
19498      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19499      */
19500     toggleSourceEdit : function(sourceEditMode){
19501         
19502         this.sourceEditMode = sourceEditMode === true;
19503         
19504         if(this.sourceEditMode){
19505  
19506             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19507             
19508         }else{
19509             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19510             //this.iframe.className = '';
19511             this.deferFocus();
19512         }
19513         //this.setSize(this.owner.wrap.getSize());
19514         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19515     },
19516
19517     
19518   
19519
19520     /**
19521      * Protected method that will not generally be called directly. If you need/want
19522      * custom HTML cleanup, this is the method you should override.
19523      * @param {String} html The HTML to be cleaned
19524      * return {String} The cleaned HTML
19525      */
19526     cleanHtml : function(html){
19527         html = String(html);
19528         if(html.length > 5){
19529             if(Roo.isSafari){ // strip safari nonsense
19530                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19531             }
19532         }
19533         if(html == '&nbsp;'){
19534             html = '';
19535         }
19536         return html;
19537     },
19538
19539     /**
19540      * HTML Editor -> Textarea
19541      * Protected method that will not generally be called directly. Syncs the contents
19542      * of the editor iframe with the textarea.
19543      */
19544     syncValue : function(){
19545         if(this.initialized){
19546             var bd = (this.doc.body || this.doc.documentElement);
19547             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19548             var html = bd.innerHTML;
19549             if(Roo.isSafari){
19550                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19551                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19552                 if(m && m[1]){
19553                     html = '<div style="'+m[0]+'">' + html + '</div>';
19554                 }
19555             }
19556             html = this.cleanHtml(html);
19557             // fix up the special chars.. normaly like back quotes in word...
19558             // however we do not want to do this with chinese..
19559             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19560                 var cc = b.charCodeAt();
19561                 if (
19562                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19563                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19564                     (cc >= 0xf900 && cc < 0xfb00 )
19565                 ) {
19566                         return b;
19567                 }
19568                 return "&#"+cc+";" 
19569             });
19570             if(this.owner.fireEvent('beforesync', this, html) !== false){
19571                 this.el.dom.value = html;
19572                 this.owner.fireEvent('sync', this, html);
19573             }
19574         }
19575     },
19576
19577     /**
19578      * Protected method that will not generally be called directly. Pushes the value of the textarea
19579      * into the iframe editor.
19580      */
19581     pushValue : function(){
19582         if(this.initialized){
19583             var v = this.el.dom.value.trim();
19584             
19585 //            if(v.length < 1){
19586 //                v = '&#160;';
19587 //            }
19588             
19589             if(this.owner.fireEvent('beforepush', this, v) !== false){
19590                 var d = (this.doc.body || this.doc.documentElement);
19591                 d.innerHTML = v;
19592                 this.cleanUpPaste();
19593                 this.el.dom.value = d.innerHTML;
19594                 this.owner.fireEvent('push', this, v);
19595             }
19596         }
19597     },
19598
19599     // private
19600     deferFocus : function(){
19601         this.focus.defer(10, this);
19602     },
19603
19604     // doc'ed in Field
19605     focus : function(){
19606         if(this.win && !this.sourceEditMode){
19607             this.win.focus();
19608         }else{
19609             this.el.focus();
19610         }
19611     },
19612     
19613     assignDocWin: function()
19614     {
19615         var iframe = this.iframe;
19616         
19617          if(Roo.isIE){
19618             this.doc = iframe.contentWindow.document;
19619             this.win = iframe.contentWindow;
19620         } else {
19621 //            if (!Roo.get(this.frameId)) {
19622 //                return;
19623 //            }
19624 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19625 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19626             
19627             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19628                 return;
19629             }
19630             
19631             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19632             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19633         }
19634     },
19635     
19636     // private
19637     initEditor : function(){
19638         //console.log("INIT EDITOR");
19639         this.assignDocWin();
19640         
19641         
19642         
19643         this.doc.designMode="on";
19644         this.doc.open();
19645         this.doc.write(this.getDocMarkup());
19646         this.doc.close();
19647         
19648         var dbody = (this.doc.body || this.doc.documentElement);
19649         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19650         // this copies styles from the containing element into thsi one..
19651         // not sure why we need all of this..
19652         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19653         
19654         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19655         //ss['background-attachment'] = 'fixed'; // w3c
19656         dbody.bgProperties = 'fixed'; // ie
19657         //Roo.DomHelper.applyStyles(dbody, ss);
19658         Roo.EventManager.on(this.doc, {
19659             //'mousedown': this.onEditorEvent,
19660             'mouseup': this.onEditorEvent,
19661             'dblclick': this.onEditorEvent,
19662             'click': this.onEditorEvent,
19663             'keyup': this.onEditorEvent,
19664             buffer:100,
19665             scope: this
19666         });
19667         if(Roo.isGecko){
19668             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19669         }
19670         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19671             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19672         }
19673         this.initialized = true;
19674
19675         this.owner.fireEvent('initialize', this);
19676         this.pushValue();
19677     },
19678
19679     // private
19680     onDestroy : function(){
19681         
19682         
19683         
19684         if(this.rendered){
19685             
19686             //for (var i =0; i < this.toolbars.length;i++) {
19687             //    // fixme - ask toolbars for heights?
19688             //    this.toolbars[i].onDestroy();
19689            // }
19690             
19691             //this.wrap.dom.innerHTML = '';
19692             //this.wrap.remove();
19693         }
19694     },
19695
19696     // private
19697     onFirstFocus : function(){
19698         
19699         this.assignDocWin();
19700         
19701         
19702         this.activated = true;
19703          
19704     
19705         if(Roo.isGecko){ // prevent silly gecko errors
19706             this.win.focus();
19707             var s = this.win.getSelection();
19708             if(!s.focusNode || s.focusNode.nodeType != 3){
19709                 var r = s.getRangeAt(0);
19710                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19711                 r.collapse(true);
19712                 this.deferFocus();
19713             }
19714             try{
19715                 this.execCmd('useCSS', true);
19716                 this.execCmd('styleWithCSS', false);
19717             }catch(e){}
19718         }
19719         this.owner.fireEvent('activate', this);
19720     },
19721
19722     // private
19723     adjustFont: function(btn){
19724         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19725         //if(Roo.isSafari){ // safari
19726         //    adjust *= 2;
19727        // }
19728         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19729         if(Roo.isSafari){ // safari
19730             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19731             v =  (v < 10) ? 10 : v;
19732             v =  (v > 48) ? 48 : v;
19733             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19734             
19735         }
19736         
19737         
19738         v = Math.max(1, v+adjust);
19739         
19740         this.execCmd('FontSize', v  );
19741     },
19742
19743     onEditorEvent : function(e)
19744     {
19745         this.owner.fireEvent('editorevent', this, e);
19746       //  this.updateToolbar();
19747         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19748     },
19749
19750     insertTag : function(tg)
19751     {
19752         // could be a bit smarter... -> wrap the current selected tRoo..
19753         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19754             
19755             range = this.createRange(this.getSelection());
19756             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19757             wrappingNode.appendChild(range.extractContents());
19758             range.insertNode(wrappingNode);
19759
19760             return;
19761             
19762             
19763             
19764         }
19765         this.execCmd("formatblock",   tg);
19766         
19767     },
19768     
19769     insertText : function(txt)
19770     {
19771         
19772         
19773         var range = this.createRange();
19774         range.deleteContents();
19775                //alert(Sender.getAttribute('label'));
19776                
19777         range.insertNode(this.doc.createTextNode(txt));
19778     } ,
19779     
19780      
19781
19782     /**
19783      * Executes a Midas editor command on the editor document and performs necessary focus and
19784      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19785      * @param {String} cmd The Midas command
19786      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19787      */
19788     relayCmd : function(cmd, value){
19789         this.win.focus();
19790         this.execCmd(cmd, value);
19791         this.owner.fireEvent('editorevent', this);
19792         //this.updateToolbar();
19793         this.owner.deferFocus();
19794     },
19795
19796     /**
19797      * Executes a Midas editor command directly on the editor document.
19798      * For visual commands, you should use {@link #relayCmd} instead.
19799      * <b>This should only be called after the editor is initialized.</b>
19800      * @param {String} cmd The Midas command
19801      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19802      */
19803     execCmd : function(cmd, value){
19804         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19805         this.syncValue();
19806     },
19807  
19808  
19809    
19810     /**
19811      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19812      * to insert tRoo.
19813      * @param {String} text | dom node.. 
19814      */
19815     insertAtCursor : function(text)
19816     {
19817         
19818         
19819         
19820         if(!this.activated){
19821             return;
19822         }
19823         /*
19824         if(Roo.isIE){
19825             this.win.focus();
19826             var r = this.doc.selection.createRange();
19827             if(r){
19828                 r.collapse(true);
19829                 r.pasteHTML(text);
19830                 this.syncValue();
19831                 this.deferFocus();
19832             
19833             }
19834             return;
19835         }
19836         */
19837         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19838             this.win.focus();
19839             
19840             
19841             // from jquery ui (MIT licenced)
19842             var range, node;
19843             var win = this.win;
19844             
19845             if (win.getSelection && win.getSelection().getRangeAt) {
19846                 range = win.getSelection().getRangeAt(0);
19847                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19848                 range.insertNode(node);
19849             } else if (win.document.selection && win.document.selection.createRange) {
19850                 // no firefox support
19851                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19852                 win.document.selection.createRange().pasteHTML(txt);
19853             } else {
19854                 // no firefox support
19855                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19856                 this.execCmd('InsertHTML', txt);
19857             } 
19858             
19859             this.syncValue();
19860             
19861             this.deferFocus();
19862         }
19863     },
19864  // private
19865     mozKeyPress : function(e){
19866         if(e.ctrlKey){
19867             var c = e.getCharCode(), cmd;
19868           
19869             if(c > 0){
19870                 c = String.fromCharCode(c).toLowerCase();
19871                 switch(c){
19872                     case 'b':
19873                         cmd = 'bold';
19874                         break;
19875                     case 'i':
19876                         cmd = 'italic';
19877                         break;
19878                     
19879                     case 'u':
19880                         cmd = 'underline';
19881                         break;
19882                     
19883                     case 'v':
19884                         this.cleanUpPaste.defer(100, this);
19885                         return;
19886                         
19887                 }
19888                 if(cmd){
19889                     this.win.focus();
19890                     this.execCmd(cmd);
19891                     this.deferFocus();
19892                     e.preventDefault();
19893                 }
19894                 
19895             }
19896         }
19897     },
19898
19899     // private
19900     fixKeys : function(){ // load time branching for fastest keydown performance
19901         if(Roo.isIE){
19902             return function(e){
19903                 var k = e.getKey(), r;
19904                 if(k == e.TAB){
19905                     e.stopEvent();
19906                     r = this.doc.selection.createRange();
19907                     if(r){
19908                         r.collapse(true);
19909                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19910                         this.deferFocus();
19911                     }
19912                     return;
19913                 }
19914                 
19915                 if(k == e.ENTER){
19916                     r = this.doc.selection.createRange();
19917                     if(r){
19918                         var target = r.parentElement();
19919                         if(!target || target.tagName.toLowerCase() != 'li'){
19920                             e.stopEvent();
19921                             r.pasteHTML('<br />');
19922                             r.collapse(false);
19923                             r.select();
19924                         }
19925                     }
19926                 }
19927                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19928                     this.cleanUpPaste.defer(100, this);
19929                     return;
19930                 }
19931                 
19932                 
19933             };
19934         }else if(Roo.isOpera){
19935             return function(e){
19936                 var k = e.getKey();
19937                 if(k == e.TAB){
19938                     e.stopEvent();
19939                     this.win.focus();
19940                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19941                     this.deferFocus();
19942                 }
19943                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19944                     this.cleanUpPaste.defer(100, this);
19945                     return;
19946                 }
19947                 
19948             };
19949         }else if(Roo.isSafari){
19950             return function(e){
19951                 var k = e.getKey();
19952                 
19953                 if(k == e.TAB){
19954                     e.stopEvent();
19955                     this.execCmd('InsertText','\t');
19956                     this.deferFocus();
19957                     return;
19958                 }
19959                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19960                     this.cleanUpPaste.defer(100, this);
19961                     return;
19962                 }
19963                 
19964              };
19965         }
19966     }(),
19967     
19968     getAllAncestors: function()
19969     {
19970         var p = this.getSelectedNode();
19971         var a = [];
19972         if (!p) {
19973             a.push(p); // push blank onto stack..
19974             p = this.getParentElement();
19975         }
19976         
19977         
19978         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19979             a.push(p);
19980             p = p.parentNode;
19981         }
19982         a.push(this.doc.body);
19983         return a;
19984     },
19985     lastSel : false,
19986     lastSelNode : false,
19987     
19988     
19989     getSelection : function() 
19990     {
19991         this.assignDocWin();
19992         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19993     },
19994     
19995     getSelectedNode: function() 
19996     {
19997         // this may only work on Gecko!!!
19998         
19999         // should we cache this!!!!
20000         
20001         
20002         
20003          
20004         var range = this.createRange(this.getSelection()).cloneRange();
20005         
20006         if (Roo.isIE) {
20007             var parent = range.parentElement();
20008             while (true) {
20009                 var testRange = range.duplicate();
20010                 testRange.moveToElementText(parent);
20011                 if (testRange.inRange(range)) {
20012                     break;
20013                 }
20014                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20015                     break;
20016                 }
20017                 parent = parent.parentElement;
20018             }
20019             return parent;
20020         }
20021         
20022         // is ancestor a text element.
20023         var ac =  range.commonAncestorContainer;
20024         if (ac.nodeType == 3) {
20025             ac = ac.parentNode;
20026         }
20027         
20028         var ar = ac.childNodes;
20029          
20030         var nodes = [];
20031         var other_nodes = [];
20032         var has_other_nodes = false;
20033         for (var i=0;i<ar.length;i++) {
20034             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20035                 continue;
20036             }
20037             // fullly contained node.
20038             
20039             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20040                 nodes.push(ar[i]);
20041                 continue;
20042             }
20043             
20044             // probably selected..
20045             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20046                 other_nodes.push(ar[i]);
20047                 continue;
20048             }
20049             // outer..
20050             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20051                 continue;
20052             }
20053             
20054             
20055             has_other_nodes = true;
20056         }
20057         if (!nodes.length && other_nodes.length) {
20058             nodes= other_nodes;
20059         }
20060         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20061             return false;
20062         }
20063         
20064         return nodes[0];
20065     },
20066     createRange: function(sel)
20067     {
20068         // this has strange effects when using with 
20069         // top toolbar - not sure if it's a great idea.
20070         //this.editor.contentWindow.focus();
20071         if (typeof sel != "undefined") {
20072             try {
20073                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20074             } catch(e) {
20075                 return this.doc.createRange();
20076             }
20077         } else {
20078             return this.doc.createRange();
20079         }
20080     },
20081     getParentElement: function()
20082     {
20083         
20084         this.assignDocWin();
20085         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20086         
20087         var range = this.createRange(sel);
20088          
20089         try {
20090             var p = range.commonAncestorContainer;
20091             while (p.nodeType == 3) { // text node
20092                 p = p.parentNode;
20093             }
20094             return p;
20095         } catch (e) {
20096             return null;
20097         }
20098     
20099     },
20100     /***
20101      *
20102      * Range intersection.. the hard stuff...
20103      *  '-1' = before
20104      *  '0' = hits..
20105      *  '1' = after.
20106      *         [ -- selected range --- ]
20107      *   [fail]                        [fail]
20108      *
20109      *    basically..
20110      *      if end is before start or  hits it. fail.
20111      *      if start is after end or hits it fail.
20112      *
20113      *   if either hits (but other is outside. - then it's not 
20114      *   
20115      *    
20116      **/
20117     
20118     
20119     // @see http://www.thismuchiknow.co.uk/?p=64.
20120     rangeIntersectsNode : function(range, node)
20121     {
20122         var nodeRange = node.ownerDocument.createRange();
20123         try {
20124             nodeRange.selectNode(node);
20125         } catch (e) {
20126             nodeRange.selectNodeContents(node);
20127         }
20128     
20129         var rangeStartRange = range.cloneRange();
20130         rangeStartRange.collapse(true);
20131     
20132         var rangeEndRange = range.cloneRange();
20133         rangeEndRange.collapse(false);
20134     
20135         var nodeStartRange = nodeRange.cloneRange();
20136         nodeStartRange.collapse(true);
20137     
20138         var nodeEndRange = nodeRange.cloneRange();
20139         nodeEndRange.collapse(false);
20140     
20141         return rangeStartRange.compareBoundaryPoints(
20142                  Range.START_TO_START, nodeEndRange) == -1 &&
20143                rangeEndRange.compareBoundaryPoints(
20144                  Range.START_TO_START, nodeStartRange) == 1;
20145         
20146          
20147     },
20148     rangeCompareNode : function(range, node)
20149     {
20150         var nodeRange = node.ownerDocument.createRange();
20151         try {
20152             nodeRange.selectNode(node);
20153         } catch (e) {
20154             nodeRange.selectNodeContents(node);
20155         }
20156         
20157         
20158         range.collapse(true);
20159     
20160         nodeRange.collapse(true);
20161      
20162         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20163         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20164          
20165         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20166         
20167         var nodeIsBefore   =  ss == 1;
20168         var nodeIsAfter    = ee == -1;
20169         
20170         if (nodeIsBefore && nodeIsAfter) {
20171             return 0; // outer
20172         }
20173         if (!nodeIsBefore && nodeIsAfter) {
20174             return 1; //right trailed.
20175         }
20176         
20177         if (nodeIsBefore && !nodeIsAfter) {
20178             return 2;  // left trailed.
20179         }
20180         // fully contined.
20181         return 3;
20182     },
20183
20184     // private? - in a new class?
20185     cleanUpPaste :  function()
20186     {
20187         // cleans up the whole document..
20188         Roo.log('cleanuppaste');
20189         
20190         this.cleanUpChildren(this.doc.body);
20191         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20192         if (clean != this.doc.body.innerHTML) {
20193             this.doc.body.innerHTML = clean;
20194         }
20195         
20196     },
20197     
20198     cleanWordChars : function(input) {// change the chars to hex code
20199         var he = Roo.HtmlEditorCore;
20200         
20201         var output = input;
20202         Roo.each(he.swapCodes, function(sw) { 
20203             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20204             
20205             output = output.replace(swapper, sw[1]);
20206         });
20207         
20208         return output;
20209     },
20210     
20211     
20212     cleanUpChildren : function (n)
20213     {
20214         if (!n.childNodes.length) {
20215             return;
20216         }
20217         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20218            this.cleanUpChild(n.childNodes[i]);
20219         }
20220     },
20221     
20222     
20223         
20224     
20225     cleanUpChild : function (node)
20226     {
20227         var ed = this;
20228         //console.log(node);
20229         if (node.nodeName == "#text") {
20230             // clean up silly Windows -- stuff?
20231             return; 
20232         }
20233         if (node.nodeName == "#comment") {
20234             node.parentNode.removeChild(node);
20235             // clean up silly Windows -- stuff?
20236             return; 
20237         }
20238         var lcname = node.tagName.toLowerCase();
20239         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20240         // whitelist of tags..
20241         
20242         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20243             // remove node.
20244             node.parentNode.removeChild(node);
20245             return;
20246             
20247         }
20248         
20249         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20250         
20251         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20252         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20253         
20254         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20255         //    remove_keep_children = true;
20256         //}
20257         
20258         if (remove_keep_children) {
20259             this.cleanUpChildren(node);
20260             // inserts everything just before this node...
20261             while (node.childNodes.length) {
20262                 var cn = node.childNodes[0];
20263                 node.removeChild(cn);
20264                 node.parentNode.insertBefore(cn, node);
20265             }
20266             node.parentNode.removeChild(node);
20267             return;
20268         }
20269         
20270         if (!node.attributes || !node.attributes.length) {
20271             this.cleanUpChildren(node);
20272             return;
20273         }
20274         
20275         function cleanAttr(n,v)
20276         {
20277             
20278             if (v.match(/^\./) || v.match(/^\//)) {
20279                 return;
20280             }
20281             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20282                 return;
20283             }
20284             if (v.match(/^#/)) {
20285                 return;
20286             }
20287 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20288             node.removeAttribute(n);
20289             
20290         }
20291         
20292         var cwhite = this.cwhite;
20293         var cblack = this.cblack;
20294             
20295         function cleanStyle(n,v)
20296         {
20297             if (v.match(/expression/)) { //XSS?? should we even bother..
20298                 node.removeAttribute(n);
20299                 return;
20300             }
20301             
20302             var parts = v.split(/;/);
20303             var clean = [];
20304             
20305             Roo.each(parts, function(p) {
20306                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20307                 if (!p.length) {
20308                     return true;
20309                 }
20310                 var l = p.split(':').shift().replace(/\s+/g,'');
20311                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20312                 
20313                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20314 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20315                     //node.removeAttribute(n);
20316                     return true;
20317                 }
20318                 //Roo.log()
20319                 // only allow 'c whitelisted system attributes'
20320                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20321 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20322                     //node.removeAttribute(n);
20323                     return true;
20324                 }
20325                 
20326                 
20327                  
20328                 
20329                 clean.push(p);
20330                 return true;
20331             });
20332             if (clean.length) { 
20333                 node.setAttribute(n, clean.join(';'));
20334             } else {
20335                 node.removeAttribute(n);
20336             }
20337             
20338         }
20339         
20340         
20341         for (var i = node.attributes.length-1; i > -1 ; i--) {
20342             var a = node.attributes[i];
20343             //console.log(a);
20344             
20345             if (a.name.toLowerCase().substr(0,2)=='on')  {
20346                 node.removeAttribute(a.name);
20347                 continue;
20348             }
20349             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20350                 node.removeAttribute(a.name);
20351                 continue;
20352             }
20353             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20354                 cleanAttr(a.name,a.value); // fixme..
20355                 continue;
20356             }
20357             if (a.name == 'style') {
20358                 cleanStyle(a.name,a.value);
20359                 continue;
20360             }
20361             /// clean up MS crap..
20362             // tecnically this should be a list of valid class'es..
20363             
20364             
20365             if (a.name == 'class') {
20366                 if (a.value.match(/^Mso/)) {
20367                     node.className = '';
20368                 }
20369                 
20370                 if (a.value.match(/body/)) {
20371                     node.className = '';
20372                 }
20373                 continue;
20374             }
20375             
20376             // style cleanup!?
20377             // class cleanup?
20378             
20379         }
20380         
20381         
20382         this.cleanUpChildren(node);
20383         
20384         
20385     },
20386     
20387     /**
20388      * Clean up MS wordisms...
20389      */
20390     cleanWord : function(node)
20391     {
20392         
20393         
20394         if (!node) {
20395             this.cleanWord(this.doc.body);
20396             return;
20397         }
20398         if (node.nodeName == "#text") {
20399             // clean up silly Windows -- stuff?
20400             return; 
20401         }
20402         if (node.nodeName == "#comment") {
20403             node.parentNode.removeChild(node);
20404             // clean up silly Windows -- stuff?
20405             return; 
20406         }
20407         
20408         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20409             node.parentNode.removeChild(node);
20410             return;
20411         }
20412         
20413         // remove - but keep children..
20414         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20415             while (node.childNodes.length) {
20416                 var cn = node.childNodes[0];
20417                 node.removeChild(cn);
20418                 node.parentNode.insertBefore(cn, node);
20419             }
20420             node.parentNode.removeChild(node);
20421             this.iterateChildren(node, this.cleanWord);
20422             return;
20423         }
20424         // clean styles
20425         if (node.className.length) {
20426             
20427             var cn = node.className.split(/\W+/);
20428             var cna = [];
20429             Roo.each(cn, function(cls) {
20430                 if (cls.match(/Mso[a-zA-Z]+/)) {
20431                     return;
20432                 }
20433                 cna.push(cls);
20434             });
20435             node.className = cna.length ? cna.join(' ') : '';
20436             if (!cna.length) {
20437                 node.removeAttribute("class");
20438             }
20439         }
20440         
20441         if (node.hasAttribute("lang")) {
20442             node.removeAttribute("lang");
20443         }
20444         
20445         if (node.hasAttribute("style")) {
20446             
20447             var styles = node.getAttribute("style").split(";");
20448             var nstyle = [];
20449             Roo.each(styles, function(s) {
20450                 if (!s.match(/:/)) {
20451                     return;
20452                 }
20453                 var kv = s.split(":");
20454                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20455                     return;
20456                 }
20457                 // what ever is left... we allow.
20458                 nstyle.push(s);
20459             });
20460             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20461             if (!nstyle.length) {
20462                 node.removeAttribute('style');
20463             }
20464         }
20465         this.iterateChildren(node, this.cleanWord);
20466         
20467         
20468         
20469     },
20470     /**
20471      * iterateChildren of a Node, calling fn each time, using this as the scole..
20472      * @param {DomNode} node node to iterate children of.
20473      * @param {Function} fn method of this class to call on each item.
20474      */
20475     iterateChildren : function(node, fn)
20476     {
20477         if (!node.childNodes.length) {
20478                 return;
20479         }
20480         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20481            fn.call(this, node.childNodes[i])
20482         }
20483     },
20484     
20485     
20486     /**
20487      * cleanTableWidths.
20488      *
20489      * Quite often pasting from word etc.. results in tables with column and widths.
20490      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20491      *
20492      */
20493     cleanTableWidths : function(node)
20494     {
20495          
20496          
20497         if (!node) {
20498             this.cleanTableWidths(this.doc.body);
20499             return;
20500         }
20501         
20502         // ignore list...
20503         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20504             return; 
20505         }
20506         Roo.log(node.tagName);
20507         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20508             this.iterateChildren(node, this.cleanTableWidths);
20509             return;
20510         }
20511         if (node.hasAttribute('width')) {
20512             node.removeAttribute('width');
20513         }
20514         
20515          
20516         if (node.hasAttribute("style")) {
20517             // pretty basic...
20518             
20519             var styles = node.getAttribute("style").split(";");
20520             var nstyle = [];
20521             Roo.each(styles, function(s) {
20522                 if (!s.match(/:/)) {
20523                     return;
20524                 }
20525                 var kv = s.split(":");
20526                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20527                     return;
20528                 }
20529                 // what ever is left... we allow.
20530                 nstyle.push(s);
20531             });
20532             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20533             if (!nstyle.length) {
20534                 node.removeAttribute('style');
20535             }
20536         }
20537         
20538         this.iterateChildren(node, this.cleanTableWidths);
20539         
20540         
20541     },
20542     
20543     
20544     
20545     
20546     domToHTML : function(currentElement, depth, nopadtext) {
20547         
20548         depth = depth || 0;
20549         nopadtext = nopadtext || false;
20550     
20551         if (!currentElement) {
20552             return this.domToHTML(this.doc.body);
20553         }
20554         
20555         //Roo.log(currentElement);
20556         var j;
20557         var allText = false;
20558         var nodeName = currentElement.nodeName;
20559         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20560         
20561         if  (nodeName == '#text') {
20562             
20563             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20564         }
20565         
20566         
20567         var ret = '';
20568         if (nodeName != 'BODY') {
20569              
20570             var i = 0;
20571             // Prints the node tagName, such as <A>, <IMG>, etc
20572             if (tagName) {
20573                 var attr = [];
20574                 for(i = 0; i < currentElement.attributes.length;i++) {
20575                     // quoting?
20576                     var aname = currentElement.attributes.item(i).name;
20577                     if (!currentElement.attributes.item(i).value.length) {
20578                         continue;
20579                     }
20580                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20581                 }
20582                 
20583                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20584             } 
20585             else {
20586                 
20587                 // eack
20588             }
20589         } else {
20590             tagName = false;
20591         }
20592         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20593             return ret;
20594         }
20595         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20596             nopadtext = true;
20597         }
20598         
20599         
20600         // Traverse the tree
20601         i = 0;
20602         var currentElementChild = currentElement.childNodes.item(i);
20603         var allText = true;
20604         var innerHTML  = '';
20605         lastnode = '';
20606         while (currentElementChild) {
20607             // Formatting code (indent the tree so it looks nice on the screen)
20608             var nopad = nopadtext;
20609             if (lastnode == 'SPAN') {
20610                 nopad  = true;
20611             }
20612             // text
20613             if  (currentElementChild.nodeName == '#text') {
20614                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20615                 toadd = nopadtext ? toadd : toadd.trim();
20616                 if (!nopad && toadd.length > 80) {
20617                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20618                 }
20619                 innerHTML  += toadd;
20620                 
20621                 i++;
20622                 currentElementChild = currentElement.childNodes.item(i);
20623                 lastNode = '';
20624                 continue;
20625             }
20626             allText = false;
20627             
20628             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20629                 
20630             // Recursively traverse the tree structure of the child node
20631             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20632             lastnode = currentElementChild.nodeName;
20633             i++;
20634             currentElementChild=currentElement.childNodes.item(i);
20635         }
20636         
20637         ret += innerHTML;
20638         
20639         if (!allText) {
20640                 // The remaining code is mostly for formatting the tree
20641             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20642         }
20643         
20644         
20645         if (tagName) {
20646             ret+= "</"+tagName+">";
20647         }
20648         return ret;
20649         
20650     },
20651         
20652     applyBlacklists : function()
20653     {
20654         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20655         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20656         
20657         this.white = [];
20658         this.black = [];
20659         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20660             if (b.indexOf(tag) > -1) {
20661                 return;
20662             }
20663             this.white.push(tag);
20664             
20665         }, this);
20666         
20667         Roo.each(w, function(tag) {
20668             if (b.indexOf(tag) > -1) {
20669                 return;
20670             }
20671             if (this.white.indexOf(tag) > -1) {
20672                 return;
20673             }
20674             this.white.push(tag);
20675             
20676         }, this);
20677         
20678         
20679         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20680             if (w.indexOf(tag) > -1) {
20681                 return;
20682             }
20683             this.black.push(tag);
20684             
20685         }, this);
20686         
20687         Roo.each(b, function(tag) {
20688             if (w.indexOf(tag) > -1) {
20689                 return;
20690             }
20691             if (this.black.indexOf(tag) > -1) {
20692                 return;
20693             }
20694             this.black.push(tag);
20695             
20696         }, this);
20697         
20698         
20699         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20700         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20701         
20702         this.cwhite = [];
20703         this.cblack = [];
20704         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20705             if (b.indexOf(tag) > -1) {
20706                 return;
20707             }
20708             this.cwhite.push(tag);
20709             
20710         }, this);
20711         
20712         Roo.each(w, function(tag) {
20713             if (b.indexOf(tag) > -1) {
20714                 return;
20715             }
20716             if (this.cwhite.indexOf(tag) > -1) {
20717                 return;
20718             }
20719             this.cwhite.push(tag);
20720             
20721         }, this);
20722         
20723         
20724         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20725             if (w.indexOf(tag) > -1) {
20726                 return;
20727             }
20728             this.cblack.push(tag);
20729             
20730         }, this);
20731         
20732         Roo.each(b, function(tag) {
20733             if (w.indexOf(tag) > -1) {
20734                 return;
20735             }
20736             if (this.cblack.indexOf(tag) > -1) {
20737                 return;
20738             }
20739             this.cblack.push(tag);
20740             
20741         }, this);
20742     },
20743     
20744     setStylesheets : function(stylesheets)
20745     {
20746         if(typeof(stylesheets) == 'string'){
20747             Roo.get(this.iframe.contentDocument.head).createChild({
20748                 tag : 'link',
20749                 rel : 'stylesheet',
20750                 type : 'text/css',
20751                 href : stylesheets
20752             });
20753             
20754             return;
20755         }
20756         var _this = this;
20757      
20758         Roo.each(stylesheets, function(s) {
20759             if(!s.length){
20760                 return;
20761             }
20762             
20763             Roo.get(_this.iframe.contentDocument.head).createChild({
20764                 tag : 'link',
20765                 rel : 'stylesheet',
20766                 type : 'text/css',
20767                 href : s
20768             });
20769         });
20770
20771         
20772     },
20773     
20774     removeStylesheets : function()
20775     {
20776         var _this = this;
20777         
20778         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20779             s.remove();
20780         });
20781     }
20782     
20783     // hide stuff that is not compatible
20784     /**
20785      * @event blur
20786      * @hide
20787      */
20788     /**
20789      * @event change
20790      * @hide
20791      */
20792     /**
20793      * @event focus
20794      * @hide
20795      */
20796     /**
20797      * @event specialkey
20798      * @hide
20799      */
20800     /**
20801      * @cfg {String} fieldClass @hide
20802      */
20803     /**
20804      * @cfg {String} focusClass @hide
20805      */
20806     /**
20807      * @cfg {String} autoCreate @hide
20808      */
20809     /**
20810      * @cfg {String} inputType @hide
20811      */
20812     /**
20813      * @cfg {String} invalidClass @hide
20814      */
20815     /**
20816      * @cfg {String} invalidText @hide
20817      */
20818     /**
20819      * @cfg {String} msgFx @hide
20820      */
20821     /**
20822      * @cfg {String} validateOnBlur @hide
20823      */
20824 });
20825
20826 Roo.HtmlEditorCore.white = [
20827         'area', 'br', 'img', 'input', 'hr', 'wbr',
20828         
20829        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20830        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20831        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20832        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20833        'table',   'ul',         'xmp', 
20834        
20835        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20836       'thead',   'tr', 
20837      
20838       'dir', 'menu', 'ol', 'ul', 'dl',
20839        
20840       'embed',  'object'
20841 ];
20842
20843
20844 Roo.HtmlEditorCore.black = [
20845     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20846         'applet', // 
20847         'base',   'basefont', 'bgsound', 'blink',  'body', 
20848         'frame',  'frameset', 'head',    'html',   'ilayer', 
20849         'iframe', 'layer',  'link',     'meta',    'object',   
20850         'script', 'style' ,'title',  'xml' // clean later..
20851 ];
20852 Roo.HtmlEditorCore.clean = [
20853     'script', 'style', 'title', 'xml'
20854 ];
20855 Roo.HtmlEditorCore.remove = [
20856     'font'
20857 ];
20858 // attributes..
20859
20860 Roo.HtmlEditorCore.ablack = [
20861     'on'
20862 ];
20863     
20864 Roo.HtmlEditorCore.aclean = [ 
20865     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20866 ];
20867
20868 // protocols..
20869 Roo.HtmlEditorCore.pwhite= [
20870         'http',  'https',  'mailto'
20871 ];
20872
20873 // white listed style attributes.
20874 Roo.HtmlEditorCore.cwhite= [
20875       //  'text-align', /// default is to allow most things..
20876       
20877          
20878 //        'font-size'//??
20879 ];
20880
20881 // black listed style attributes.
20882 Roo.HtmlEditorCore.cblack= [
20883       //  'font-size' -- this can be set by the project 
20884 ];
20885
20886
20887 Roo.HtmlEditorCore.swapCodes   =[ 
20888     [    8211, "--" ], 
20889     [    8212, "--" ], 
20890     [    8216,  "'" ],  
20891     [    8217, "'" ],  
20892     [    8220, '"' ],  
20893     [    8221, '"' ],  
20894     [    8226, "*" ],  
20895     [    8230, "..." ]
20896 ]; 
20897
20898     /*
20899  * - LGPL
20900  *
20901  * HtmlEditor
20902  * 
20903  */
20904
20905 /**
20906  * @class Roo.bootstrap.HtmlEditor
20907  * @extends Roo.bootstrap.TextArea
20908  * Bootstrap HtmlEditor class
20909
20910  * @constructor
20911  * Create a new HtmlEditor
20912  * @param {Object} config The config object
20913  */
20914
20915 Roo.bootstrap.HtmlEditor = function(config){
20916     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20917     if (!this.toolbars) {
20918         this.toolbars = [];
20919     }
20920     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20921     this.addEvents({
20922             /**
20923              * @event initialize
20924              * Fires when the editor is fully initialized (including the iframe)
20925              * @param {HtmlEditor} this
20926              */
20927             initialize: true,
20928             /**
20929              * @event activate
20930              * Fires when the editor is first receives the focus. Any insertion must wait
20931              * until after this event.
20932              * @param {HtmlEditor} this
20933              */
20934             activate: true,
20935              /**
20936              * @event beforesync
20937              * Fires before the textarea is updated with content from the editor iframe. Return false
20938              * to cancel the sync.
20939              * @param {HtmlEditor} this
20940              * @param {String} html
20941              */
20942             beforesync: true,
20943              /**
20944              * @event beforepush
20945              * Fires before the iframe editor is updated with content from the textarea. Return false
20946              * to cancel the push.
20947              * @param {HtmlEditor} this
20948              * @param {String} html
20949              */
20950             beforepush: true,
20951              /**
20952              * @event sync
20953              * Fires when the textarea is updated with content from the editor iframe.
20954              * @param {HtmlEditor} this
20955              * @param {String} html
20956              */
20957             sync: true,
20958              /**
20959              * @event push
20960              * Fires when the iframe editor is updated with content from the textarea.
20961              * @param {HtmlEditor} this
20962              * @param {String} html
20963              */
20964             push: true,
20965              /**
20966              * @event editmodechange
20967              * Fires when the editor switches edit modes
20968              * @param {HtmlEditor} this
20969              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20970              */
20971             editmodechange: true,
20972             /**
20973              * @event editorevent
20974              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20975              * @param {HtmlEditor} this
20976              */
20977             editorevent: true,
20978             /**
20979              * @event firstfocus
20980              * Fires when on first focus - needed by toolbars..
20981              * @param {HtmlEditor} this
20982              */
20983             firstfocus: true,
20984             /**
20985              * @event autosave
20986              * Auto save the htmlEditor value as a file into Events
20987              * @param {HtmlEditor} this
20988              */
20989             autosave: true,
20990             /**
20991              * @event savedpreview
20992              * preview the saved version of htmlEditor
20993              * @param {HtmlEditor} this
20994              */
20995             savedpreview: true
20996         });
20997 };
20998
20999
21000 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21001     
21002     
21003       /**
21004      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21005      */
21006     toolbars : false,
21007    
21008      /**
21009      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21010      *                        Roo.resizable.
21011      */
21012     resizable : false,
21013      /**
21014      * @cfg {Number} height (in pixels)
21015      */   
21016     height: 300,
21017    /**
21018      * @cfg {Number} width (in pixels)
21019      */   
21020     width: false,
21021     
21022     /**
21023      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21024      * 
21025      */
21026     stylesheets: false,
21027     
21028     // id of frame..
21029     frameId: false,
21030     
21031     // private properties
21032     validationEvent : false,
21033     deferHeight: true,
21034     initialized : false,
21035     activated : false,
21036     
21037     onFocus : Roo.emptyFn,
21038     iframePad:3,
21039     hideMode:'offsets',
21040     
21041     
21042     tbContainer : false,
21043     
21044     toolbarContainer :function() {
21045         return this.wrap.select('.x-html-editor-tb',true).first();
21046     },
21047
21048     /**
21049      * Protected method that will not generally be called directly. It
21050      * is called when the editor creates its toolbar. Override this method if you need to
21051      * add custom toolbar buttons.
21052      * @param {HtmlEditor} editor
21053      */
21054     createToolbar : function(){
21055         
21056         Roo.log("create toolbars");
21057         
21058         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21059         this.toolbars[0].render(this.toolbarContainer());
21060         
21061         return;
21062         
21063 //        if (!editor.toolbars || !editor.toolbars.length) {
21064 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21065 //        }
21066 //        
21067 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21068 //            editor.toolbars[i] = Roo.factory(
21069 //                    typeof(editor.toolbars[i]) == 'string' ?
21070 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21071 //                Roo.bootstrap.HtmlEditor);
21072 //            editor.toolbars[i].init(editor);
21073 //        }
21074     },
21075
21076      
21077     // private
21078     onRender : function(ct, position)
21079     {
21080        // Roo.log("Call onRender: " + this.xtype);
21081         var _t = this;
21082         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21083       
21084         this.wrap = this.inputEl().wrap({
21085             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21086         });
21087         
21088         this.editorcore.onRender(ct, position);
21089          
21090         if (this.resizable) {
21091             this.resizeEl = new Roo.Resizable(this.wrap, {
21092                 pinned : true,
21093                 wrap: true,
21094                 dynamic : true,
21095                 minHeight : this.height,
21096                 height: this.height,
21097                 handles : this.resizable,
21098                 width: this.width,
21099                 listeners : {
21100                     resize : function(r, w, h) {
21101                         _t.onResize(w,h); // -something
21102                     }
21103                 }
21104             });
21105             
21106         }
21107         this.createToolbar(this);
21108        
21109         
21110         if(!this.width && this.resizable){
21111             this.setSize(this.wrap.getSize());
21112         }
21113         if (this.resizeEl) {
21114             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21115             // should trigger onReize..
21116         }
21117         
21118     },
21119
21120     // private
21121     onResize : function(w, h)
21122     {
21123         Roo.log('resize: ' +w + ',' + h );
21124         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21125         var ew = false;
21126         var eh = false;
21127         
21128         if(this.inputEl() ){
21129             if(typeof w == 'number'){
21130                 var aw = w - this.wrap.getFrameWidth('lr');
21131                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21132                 ew = aw;
21133             }
21134             if(typeof h == 'number'){
21135                  var tbh = -11;  // fixme it needs to tool bar size!
21136                 for (var i =0; i < this.toolbars.length;i++) {
21137                     // fixme - ask toolbars for heights?
21138                     tbh += this.toolbars[i].el.getHeight();
21139                     //if (this.toolbars[i].footer) {
21140                     //    tbh += this.toolbars[i].footer.el.getHeight();
21141                     //}
21142                 }
21143               
21144                 
21145                 
21146                 
21147                 
21148                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21149                 ah -= 5; // knock a few pixes off for look..
21150                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21151                 var eh = ah;
21152             }
21153         }
21154         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21155         this.editorcore.onResize(ew,eh);
21156         
21157     },
21158
21159     /**
21160      * Toggles the editor between standard and source edit mode.
21161      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21162      */
21163     toggleSourceEdit : function(sourceEditMode)
21164     {
21165         this.editorcore.toggleSourceEdit(sourceEditMode);
21166         
21167         if(this.editorcore.sourceEditMode){
21168             Roo.log('editor - showing textarea');
21169             
21170 //            Roo.log('in');
21171 //            Roo.log(this.syncValue());
21172             this.syncValue();
21173             this.inputEl().removeClass(['hide', 'x-hidden']);
21174             this.inputEl().dom.removeAttribute('tabIndex');
21175             this.inputEl().focus();
21176         }else{
21177             Roo.log('editor - hiding textarea');
21178 //            Roo.log('out')
21179 //            Roo.log(this.pushValue()); 
21180             this.pushValue();
21181             
21182             this.inputEl().addClass(['hide', 'x-hidden']);
21183             this.inputEl().dom.setAttribute('tabIndex', -1);
21184             //this.deferFocus();
21185         }
21186          
21187         if(this.resizable){
21188             this.setSize(this.wrap.getSize());
21189         }
21190         
21191         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21192     },
21193  
21194     // private (for BoxComponent)
21195     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21196
21197     // private (for BoxComponent)
21198     getResizeEl : function(){
21199         return this.wrap;
21200     },
21201
21202     // private (for BoxComponent)
21203     getPositionEl : function(){
21204         return this.wrap;
21205     },
21206
21207     // private
21208     initEvents : function(){
21209         this.originalValue = this.getValue();
21210     },
21211
21212 //    /**
21213 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21214 //     * @method
21215 //     */
21216 //    markInvalid : Roo.emptyFn,
21217 //    /**
21218 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21219 //     * @method
21220 //     */
21221 //    clearInvalid : Roo.emptyFn,
21222
21223     setValue : function(v){
21224         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21225         this.editorcore.pushValue();
21226     },
21227
21228      
21229     // private
21230     deferFocus : function(){
21231         this.focus.defer(10, this);
21232     },
21233
21234     // doc'ed in Field
21235     focus : function(){
21236         this.editorcore.focus();
21237         
21238     },
21239       
21240
21241     // private
21242     onDestroy : function(){
21243         
21244         
21245         
21246         if(this.rendered){
21247             
21248             for (var i =0; i < this.toolbars.length;i++) {
21249                 // fixme - ask toolbars for heights?
21250                 this.toolbars[i].onDestroy();
21251             }
21252             
21253             this.wrap.dom.innerHTML = '';
21254             this.wrap.remove();
21255         }
21256     },
21257
21258     // private
21259     onFirstFocus : function(){
21260         //Roo.log("onFirstFocus");
21261         this.editorcore.onFirstFocus();
21262          for (var i =0; i < this.toolbars.length;i++) {
21263             this.toolbars[i].onFirstFocus();
21264         }
21265         
21266     },
21267     
21268     // private
21269     syncValue : function()
21270     {   
21271         this.editorcore.syncValue();
21272     },
21273     
21274     pushValue : function()
21275     {   
21276         this.editorcore.pushValue();
21277     }
21278      
21279     
21280     // hide stuff that is not compatible
21281     /**
21282      * @event blur
21283      * @hide
21284      */
21285     /**
21286      * @event change
21287      * @hide
21288      */
21289     /**
21290      * @event focus
21291      * @hide
21292      */
21293     /**
21294      * @event specialkey
21295      * @hide
21296      */
21297     /**
21298      * @cfg {String} fieldClass @hide
21299      */
21300     /**
21301      * @cfg {String} focusClass @hide
21302      */
21303     /**
21304      * @cfg {String} autoCreate @hide
21305      */
21306     /**
21307      * @cfg {String} inputType @hide
21308      */
21309     /**
21310      * @cfg {String} invalidClass @hide
21311      */
21312     /**
21313      * @cfg {String} invalidText @hide
21314      */
21315     /**
21316      * @cfg {String} msgFx @hide
21317      */
21318     /**
21319      * @cfg {String} validateOnBlur @hide
21320      */
21321 });
21322  
21323     
21324    
21325    
21326    
21327       
21328 Roo.namespace('Roo.bootstrap.htmleditor');
21329 /**
21330  * @class Roo.bootstrap.HtmlEditorToolbar1
21331  * Basic Toolbar
21332  * 
21333  * Usage:
21334  *
21335  new Roo.bootstrap.HtmlEditor({
21336     ....
21337     toolbars : [
21338         new Roo.bootstrap.HtmlEditorToolbar1({
21339             disable : { fonts: 1 , format: 1, ..., ... , ...],
21340             btns : [ .... ]
21341         })
21342     }
21343      
21344  * 
21345  * @cfg {Object} disable List of elements to disable..
21346  * @cfg {Array} btns List of additional buttons.
21347  * 
21348  * 
21349  * NEEDS Extra CSS? 
21350  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21351  */
21352  
21353 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21354 {
21355     
21356     Roo.apply(this, config);
21357     
21358     // default disabled, based on 'good practice'..
21359     this.disable = this.disable || {};
21360     Roo.applyIf(this.disable, {
21361         fontSize : true,
21362         colors : true,
21363         specialElements : true
21364     });
21365     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21366     
21367     this.editor = config.editor;
21368     this.editorcore = config.editor.editorcore;
21369     
21370     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21371     
21372     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21373     // dont call parent... till later.
21374 }
21375 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21376      
21377     bar : true,
21378     
21379     editor : false,
21380     editorcore : false,
21381     
21382     
21383     formats : [
21384         "p" ,  
21385         "h1","h2","h3","h4","h5","h6", 
21386         "pre", "code", 
21387         "abbr", "acronym", "address", "cite", "samp", "var",
21388         'div','span'
21389     ],
21390     
21391     onRender : function(ct, position)
21392     {
21393        // Roo.log("Call onRender: " + this.xtype);
21394         
21395        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21396        Roo.log(this.el);
21397        this.el.dom.style.marginBottom = '0';
21398        var _this = this;
21399        var editorcore = this.editorcore;
21400        var editor= this.editor;
21401        
21402        var children = [];
21403        var btn = function(id,cmd , toggle, handler){
21404        
21405             var  event = toggle ? 'toggle' : 'click';
21406        
21407             var a = {
21408                 size : 'sm',
21409                 xtype: 'Button',
21410                 xns: Roo.bootstrap,
21411                 glyphicon : id,
21412                 cmd : id || cmd,
21413                 enableToggle:toggle !== false,
21414                 //html : 'submit'
21415                 pressed : toggle ? false : null,
21416                 listeners : {}
21417             };
21418             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21419                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21420             };
21421             children.push(a);
21422             return a;
21423        }
21424         
21425         var style = {
21426                 xtype: 'Button',
21427                 size : 'sm',
21428                 xns: Roo.bootstrap,
21429                 glyphicon : 'font',
21430                 //html : 'submit'
21431                 menu : {
21432                     xtype: 'Menu',
21433                     xns: Roo.bootstrap,
21434                     items:  []
21435                 }
21436         };
21437         Roo.each(this.formats, function(f) {
21438             style.menu.items.push({
21439                 xtype :'MenuItem',
21440                 xns: Roo.bootstrap,
21441                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21442                 tagname : f,
21443                 listeners : {
21444                     click : function()
21445                     {
21446                         editorcore.insertTag(this.tagname);
21447                         editor.focus();
21448                     }
21449                 }
21450                 
21451             });
21452         });
21453          children.push(style);   
21454             
21455             
21456         btn('bold',false,true);
21457         btn('italic',false,true);
21458         btn('align-left', 'justifyleft',true);
21459         btn('align-center', 'justifycenter',true);
21460         btn('align-right' , 'justifyright',true);
21461         btn('link', false, false, function(btn) {
21462             //Roo.log("create link?");
21463             var url = prompt(this.createLinkText, this.defaultLinkValue);
21464             if(url && url != 'http:/'+'/'){
21465                 this.editorcore.relayCmd('createlink', url);
21466             }
21467         }),
21468         btn('list','insertunorderedlist',true);
21469         btn('pencil', false,true, function(btn){
21470                 Roo.log(this);
21471                 
21472                 this.toggleSourceEdit(btn.pressed);
21473         });
21474         /*
21475         var cog = {
21476                 xtype: 'Button',
21477                 size : 'sm',
21478                 xns: Roo.bootstrap,
21479                 glyphicon : 'cog',
21480                 //html : 'submit'
21481                 menu : {
21482                     xtype: 'Menu',
21483                     xns: Roo.bootstrap,
21484                     items:  []
21485                 }
21486         };
21487         
21488         cog.menu.items.push({
21489             xtype :'MenuItem',
21490             xns: Roo.bootstrap,
21491             html : Clean styles,
21492             tagname : f,
21493             listeners : {
21494                 click : function()
21495                 {
21496                     editorcore.insertTag(this.tagname);
21497                     editor.focus();
21498                 }
21499             }
21500             
21501         });
21502        */
21503         
21504          
21505        this.xtype = 'NavSimplebar';
21506         
21507         for(var i=0;i< children.length;i++) {
21508             
21509             this.buttons.add(this.addxtypeChild(children[i]));
21510             
21511         }
21512         
21513         editor.on('editorevent', this.updateToolbar, this);
21514     },
21515     onBtnClick : function(id)
21516     {
21517        this.editorcore.relayCmd(id);
21518        this.editorcore.focus();
21519     },
21520     
21521     /**
21522      * Protected method that will not generally be called directly. It triggers
21523      * a toolbar update by reading the markup state of the current selection in the editor.
21524      */
21525     updateToolbar: function(){
21526
21527         if(!this.editorcore.activated){
21528             this.editor.onFirstFocus(); // is this neeed?
21529             return;
21530         }
21531
21532         var btns = this.buttons; 
21533         var doc = this.editorcore.doc;
21534         btns.get('bold').setActive(doc.queryCommandState('bold'));
21535         btns.get('italic').setActive(doc.queryCommandState('italic'));
21536         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21537         
21538         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21539         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21540         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21541         
21542         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21543         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21544          /*
21545         
21546         var ans = this.editorcore.getAllAncestors();
21547         if (this.formatCombo) {
21548             
21549             
21550             var store = this.formatCombo.store;
21551             this.formatCombo.setValue("");
21552             for (var i =0; i < ans.length;i++) {
21553                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21554                     // select it..
21555                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21556                     break;
21557                 }
21558             }
21559         }
21560         
21561         
21562         
21563         // hides menus... - so this cant be on a menu...
21564         Roo.bootstrap.MenuMgr.hideAll();
21565         */
21566         Roo.bootstrap.MenuMgr.hideAll();
21567         //this.editorsyncValue();
21568     },
21569     onFirstFocus: function() {
21570         this.buttons.each(function(item){
21571            item.enable();
21572         });
21573     },
21574     toggleSourceEdit : function(sourceEditMode){
21575         
21576           
21577         if(sourceEditMode){
21578             Roo.log("disabling buttons");
21579            this.buttons.each( function(item){
21580                 if(item.cmd != 'pencil'){
21581                     item.disable();
21582                 }
21583             });
21584           
21585         }else{
21586             Roo.log("enabling buttons");
21587             if(this.editorcore.initialized){
21588                 this.buttons.each( function(item){
21589                     item.enable();
21590                 });
21591             }
21592             
21593         }
21594         Roo.log("calling toggole on editor");
21595         // tell the editor that it's been pressed..
21596         this.editor.toggleSourceEdit(sourceEditMode);
21597        
21598     }
21599 });
21600
21601
21602
21603
21604
21605 /**
21606  * @class Roo.bootstrap.Table.AbstractSelectionModel
21607  * @extends Roo.util.Observable
21608  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21609  * implemented by descendant classes.  This class should not be directly instantiated.
21610  * @constructor
21611  */
21612 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21613     this.locked = false;
21614     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21615 };
21616
21617
21618 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21619     /** @ignore Called by the grid automatically. Do not call directly. */
21620     init : function(grid){
21621         this.grid = grid;
21622         this.initEvents();
21623     },
21624
21625     /**
21626      * Locks the selections.
21627      */
21628     lock : function(){
21629         this.locked = true;
21630     },
21631
21632     /**
21633      * Unlocks the selections.
21634      */
21635     unlock : function(){
21636         this.locked = false;
21637     },
21638
21639     /**
21640      * Returns true if the selections are locked.
21641      * @return {Boolean}
21642      */
21643     isLocked : function(){
21644         return this.locked;
21645     }
21646 });
21647 /**
21648  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21649  * @class Roo.bootstrap.Table.RowSelectionModel
21650  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21651  * It supports multiple selections and keyboard selection/navigation. 
21652  * @constructor
21653  * @param {Object} config
21654  */
21655
21656 Roo.bootstrap.Table.RowSelectionModel = function(config){
21657     Roo.apply(this, config);
21658     this.selections = new Roo.util.MixedCollection(false, function(o){
21659         return o.id;
21660     });
21661
21662     this.last = false;
21663     this.lastActive = false;
21664
21665     this.addEvents({
21666         /**
21667              * @event selectionchange
21668              * Fires when the selection changes
21669              * @param {SelectionModel} this
21670              */
21671             "selectionchange" : true,
21672         /**
21673              * @event afterselectionchange
21674              * Fires after the selection changes (eg. by key press or clicking)
21675              * @param {SelectionModel} this
21676              */
21677             "afterselectionchange" : true,
21678         /**
21679              * @event beforerowselect
21680              * Fires when a row is selected being selected, return false to cancel.
21681              * @param {SelectionModel} this
21682              * @param {Number} rowIndex The selected index
21683              * @param {Boolean} keepExisting False if other selections will be cleared
21684              */
21685             "beforerowselect" : true,
21686         /**
21687              * @event rowselect
21688              * Fires when a row is selected.
21689              * @param {SelectionModel} this
21690              * @param {Number} rowIndex The selected index
21691              * @param {Roo.data.Record} r The record
21692              */
21693             "rowselect" : true,
21694         /**
21695              * @event rowdeselect
21696              * Fires when a row is deselected.
21697              * @param {SelectionModel} this
21698              * @param {Number} rowIndex The selected index
21699              */
21700         "rowdeselect" : true
21701     });
21702     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21703     this.locked = false;
21704 };
21705
21706 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21707     /**
21708      * @cfg {Boolean} singleSelect
21709      * True to allow selection of only one row at a time (defaults to false)
21710      */
21711     singleSelect : false,
21712
21713     // private
21714     initEvents : function(){
21715
21716         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21717             this.grid.on("mousedown", this.handleMouseDown, this);
21718         }else{ // allow click to work like normal
21719             this.grid.on("rowclick", this.handleDragableRowClick, this);
21720         }
21721
21722         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21723             "up" : function(e){
21724                 if(!e.shiftKey){
21725                     this.selectPrevious(e.shiftKey);
21726                 }else if(this.last !== false && this.lastActive !== false){
21727                     var last = this.last;
21728                     this.selectRange(this.last,  this.lastActive-1);
21729                     this.grid.getView().focusRow(this.lastActive);
21730                     if(last !== false){
21731                         this.last = last;
21732                     }
21733                 }else{
21734                     this.selectFirstRow();
21735                 }
21736                 this.fireEvent("afterselectionchange", this);
21737             },
21738             "down" : function(e){
21739                 if(!e.shiftKey){
21740                     this.selectNext(e.shiftKey);
21741                 }else if(this.last !== false && this.lastActive !== false){
21742                     var last = this.last;
21743                     this.selectRange(this.last,  this.lastActive+1);
21744                     this.grid.getView().focusRow(this.lastActive);
21745                     if(last !== false){
21746                         this.last = last;
21747                     }
21748                 }else{
21749                     this.selectFirstRow();
21750                 }
21751                 this.fireEvent("afterselectionchange", this);
21752             },
21753             scope: this
21754         });
21755
21756         var view = this.grid.view;
21757         view.on("refresh", this.onRefresh, this);
21758         view.on("rowupdated", this.onRowUpdated, this);
21759         view.on("rowremoved", this.onRemove, this);
21760     },
21761
21762     // private
21763     onRefresh : function(){
21764         var ds = this.grid.dataSource, i, v = this.grid.view;
21765         var s = this.selections;
21766         s.each(function(r){
21767             if((i = ds.indexOfId(r.id)) != -1){
21768                 v.onRowSelect(i);
21769             }else{
21770                 s.remove(r);
21771             }
21772         });
21773     },
21774
21775     // private
21776     onRemove : function(v, index, r){
21777         this.selections.remove(r);
21778     },
21779
21780     // private
21781     onRowUpdated : function(v, index, r){
21782         if(this.isSelected(r)){
21783             v.onRowSelect(index);
21784         }
21785     },
21786
21787     /**
21788      * Select records.
21789      * @param {Array} records The records to select
21790      * @param {Boolean} keepExisting (optional) True to keep existing selections
21791      */
21792     selectRecords : function(records, keepExisting){
21793         if(!keepExisting){
21794             this.clearSelections();
21795         }
21796         var ds = this.grid.dataSource;
21797         for(var i = 0, len = records.length; i < len; i++){
21798             this.selectRow(ds.indexOf(records[i]), true);
21799         }
21800     },
21801
21802     /**
21803      * Gets the number of selected rows.
21804      * @return {Number}
21805      */
21806     getCount : function(){
21807         return this.selections.length;
21808     },
21809
21810     /**
21811      * Selects the first row in the grid.
21812      */
21813     selectFirstRow : function(){
21814         this.selectRow(0);
21815     },
21816
21817     /**
21818      * Select the last row.
21819      * @param {Boolean} keepExisting (optional) True to keep existing selections
21820      */
21821     selectLastRow : function(keepExisting){
21822         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21823     },
21824
21825     /**
21826      * Selects the row immediately following the last selected row.
21827      * @param {Boolean} keepExisting (optional) True to keep existing selections
21828      */
21829     selectNext : function(keepExisting){
21830         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21831             this.selectRow(this.last+1, keepExisting);
21832             this.grid.getView().focusRow(this.last);
21833         }
21834     },
21835
21836     /**
21837      * Selects the row that precedes the last selected row.
21838      * @param {Boolean} keepExisting (optional) True to keep existing selections
21839      */
21840     selectPrevious : function(keepExisting){
21841         if(this.last){
21842             this.selectRow(this.last-1, keepExisting);
21843             this.grid.getView().focusRow(this.last);
21844         }
21845     },
21846
21847     /**
21848      * Returns the selected records
21849      * @return {Array} Array of selected records
21850      */
21851     getSelections : function(){
21852         return [].concat(this.selections.items);
21853     },
21854
21855     /**
21856      * Returns the first selected record.
21857      * @return {Record}
21858      */
21859     getSelected : function(){
21860         return this.selections.itemAt(0);
21861     },
21862
21863
21864     /**
21865      * Clears all selections.
21866      */
21867     clearSelections : function(fast){
21868         if(this.locked) {
21869             return;
21870         }
21871         if(fast !== true){
21872             var ds = this.grid.dataSource;
21873             var s = this.selections;
21874             s.each(function(r){
21875                 this.deselectRow(ds.indexOfId(r.id));
21876             }, this);
21877             s.clear();
21878         }else{
21879             this.selections.clear();
21880         }
21881         this.last = false;
21882     },
21883
21884
21885     /**
21886      * Selects all rows.
21887      */
21888     selectAll : function(){
21889         if(this.locked) {
21890             return;
21891         }
21892         this.selections.clear();
21893         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21894             this.selectRow(i, true);
21895         }
21896     },
21897
21898     /**
21899      * Returns True if there is a selection.
21900      * @return {Boolean}
21901      */
21902     hasSelection : function(){
21903         return this.selections.length > 0;
21904     },
21905
21906     /**
21907      * Returns True if the specified row is selected.
21908      * @param {Number/Record} record The record or index of the record to check
21909      * @return {Boolean}
21910      */
21911     isSelected : function(index){
21912         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21913         return (r && this.selections.key(r.id) ? true : false);
21914     },
21915
21916     /**
21917      * Returns True if the specified record id is selected.
21918      * @param {String} id The id of record to check
21919      * @return {Boolean}
21920      */
21921     isIdSelected : function(id){
21922         return (this.selections.key(id) ? true : false);
21923     },
21924
21925     // private
21926     handleMouseDown : function(e, t){
21927         var view = this.grid.getView(), rowIndex;
21928         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21929             return;
21930         };
21931         if(e.shiftKey && this.last !== false){
21932             var last = this.last;
21933             this.selectRange(last, rowIndex, e.ctrlKey);
21934             this.last = last; // reset the last
21935             view.focusRow(rowIndex);
21936         }else{
21937             var isSelected = this.isSelected(rowIndex);
21938             if(e.button !== 0 && isSelected){
21939                 view.focusRow(rowIndex);
21940             }else if(e.ctrlKey && isSelected){
21941                 this.deselectRow(rowIndex);
21942             }else if(!isSelected){
21943                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21944                 view.focusRow(rowIndex);
21945             }
21946         }
21947         this.fireEvent("afterselectionchange", this);
21948     },
21949     // private
21950     handleDragableRowClick :  function(grid, rowIndex, e) 
21951     {
21952         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21953             this.selectRow(rowIndex, false);
21954             grid.view.focusRow(rowIndex);
21955              this.fireEvent("afterselectionchange", this);
21956         }
21957     },
21958     
21959     /**
21960      * Selects multiple rows.
21961      * @param {Array} rows Array of the indexes of the row to select
21962      * @param {Boolean} keepExisting (optional) True to keep existing selections
21963      */
21964     selectRows : function(rows, keepExisting){
21965         if(!keepExisting){
21966             this.clearSelections();
21967         }
21968         for(var i = 0, len = rows.length; i < len; i++){
21969             this.selectRow(rows[i], true);
21970         }
21971     },
21972
21973     /**
21974      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21975      * @param {Number} startRow The index of the first row in the range
21976      * @param {Number} endRow The index of the last row in the range
21977      * @param {Boolean} keepExisting (optional) True to retain existing selections
21978      */
21979     selectRange : function(startRow, endRow, keepExisting){
21980         if(this.locked) {
21981             return;
21982         }
21983         if(!keepExisting){
21984             this.clearSelections();
21985         }
21986         if(startRow <= endRow){
21987             for(var i = startRow; i <= endRow; i++){
21988                 this.selectRow(i, true);
21989             }
21990         }else{
21991             for(var i = startRow; i >= endRow; i--){
21992                 this.selectRow(i, true);
21993             }
21994         }
21995     },
21996
21997     /**
21998      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21999      * @param {Number} startRow The index of the first row in the range
22000      * @param {Number} endRow The index of the last row in the range
22001      */
22002     deselectRange : function(startRow, endRow, preventViewNotify){
22003         if(this.locked) {
22004             return;
22005         }
22006         for(var i = startRow; i <= endRow; i++){
22007             this.deselectRow(i, preventViewNotify);
22008         }
22009     },
22010
22011     /**
22012      * Selects a row.
22013      * @param {Number} row The index of the row to select
22014      * @param {Boolean} keepExisting (optional) True to keep existing selections
22015      */
22016     selectRow : function(index, keepExisting, preventViewNotify){
22017         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22018             return;
22019         }
22020         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22021             if(!keepExisting || this.singleSelect){
22022                 this.clearSelections();
22023             }
22024             var r = this.grid.dataSource.getAt(index);
22025             this.selections.add(r);
22026             this.last = this.lastActive = index;
22027             if(!preventViewNotify){
22028                 this.grid.getView().onRowSelect(index);
22029             }
22030             this.fireEvent("rowselect", this, index, r);
22031             this.fireEvent("selectionchange", this);
22032         }
22033     },
22034
22035     /**
22036      * Deselects a row.
22037      * @param {Number} row The index of the row to deselect
22038      */
22039     deselectRow : function(index, preventViewNotify){
22040         if(this.locked) {
22041             return;
22042         }
22043         if(this.last == index){
22044             this.last = false;
22045         }
22046         if(this.lastActive == index){
22047             this.lastActive = false;
22048         }
22049         var r = this.grid.dataSource.getAt(index);
22050         this.selections.remove(r);
22051         if(!preventViewNotify){
22052             this.grid.getView().onRowDeselect(index);
22053         }
22054         this.fireEvent("rowdeselect", this, index);
22055         this.fireEvent("selectionchange", this);
22056     },
22057
22058     // private
22059     restoreLast : function(){
22060         if(this._last){
22061             this.last = this._last;
22062         }
22063     },
22064
22065     // private
22066     acceptsNav : function(row, col, cm){
22067         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22068     },
22069
22070     // private
22071     onEditorKey : function(field, e){
22072         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22073         if(k == e.TAB){
22074             e.stopEvent();
22075             ed.completeEdit();
22076             if(e.shiftKey){
22077                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22078             }else{
22079                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22080             }
22081         }else if(k == e.ENTER && !e.ctrlKey){
22082             e.stopEvent();
22083             ed.completeEdit();
22084             if(e.shiftKey){
22085                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22086             }else{
22087                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22088             }
22089         }else if(k == e.ESC){
22090             ed.cancelEdit();
22091         }
22092         if(newCell){
22093             g.startEditing(newCell[0], newCell[1]);
22094         }
22095     }
22096 });/*
22097  * Based on:
22098  * Ext JS Library 1.1.1
22099  * Copyright(c) 2006-2007, Ext JS, LLC.
22100  *
22101  * Originally Released Under LGPL - original licence link has changed is not relivant.
22102  *
22103  * Fork - LGPL
22104  * <script type="text/javascript">
22105  */
22106  
22107 /**
22108  * @class Roo.bootstrap.PagingToolbar
22109  * @extends Roo.bootstrap.NavSimplebar
22110  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22111  * @constructor
22112  * Create a new PagingToolbar
22113  * @param {Object} config The config object
22114  * @param {Roo.data.Store} store
22115  */
22116 Roo.bootstrap.PagingToolbar = function(config)
22117 {
22118     // old args format still supported... - xtype is prefered..
22119         // created from xtype...
22120     
22121     this.ds = config.dataSource;
22122     
22123     if (config.store && !this.ds) {
22124         this.store= Roo.factory(config.store, Roo.data);
22125         this.ds = this.store;
22126         this.ds.xmodule = this.xmodule || false;
22127     }
22128     
22129     this.toolbarItems = [];
22130     if (config.items) {
22131         this.toolbarItems = config.items;
22132     }
22133     
22134     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22135     
22136     this.cursor = 0;
22137     
22138     if (this.ds) { 
22139         this.bind(this.ds);
22140     }
22141     
22142     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22143     
22144 };
22145
22146 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22147     /**
22148      * @cfg {Roo.data.Store} dataSource
22149      * The underlying data store providing the paged data
22150      */
22151     /**
22152      * @cfg {String/HTMLElement/Element} container
22153      * container The id or element that will contain the toolbar
22154      */
22155     /**
22156      * @cfg {Boolean} displayInfo
22157      * True to display the displayMsg (defaults to false)
22158      */
22159     /**
22160      * @cfg {Number} pageSize
22161      * The number of records to display per page (defaults to 20)
22162      */
22163     pageSize: 20,
22164     /**
22165      * @cfg {String} displayMsg
22166      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22167      */
22168     displayMsg : 'Displaying {0} - {1} of {2}',
22169     /**
22170      * @cfg {String} emptyMsg
22171      * The message to display when no records are found (defaults to "No data to display")
22172      */
22173     emptyMsg : 'No data to display',
22174     /**
22175      * Customizable piece of the default paging text (defaults to "Page")
22176      * @type String
22177      */
22178     beforePageText : "Page",
22179     /**
22180      * Customizable piece of the default paging text (defaults to "of %0")
22181      * @type String
22182      */
22183     afterPageText : "of {0}",
22184     /**
22185      * Customizable piece of the default paging text (defaults to "First Page")
22186      * @type String
22187      */
22188     firstText : "First Page",
22189     /**
22190      * Customizable piece of the default paging text (defaults to "Previous Page")
22191      * @type String
22192      */
22193     prevText : "Previous Page",
22194     /**
22195      * Customizable piece of the default paging text (defaults to "Next Page")
22196      * @type String
22197      */
22198     nextText : "Next Page",
22199     /**
22200      * Customizable piece of the default paging text (defaults to "Last Page")
22201      * @type String
22202      */
22203     lastText : "Last Page",
22204     /**
22205      * Customizable piece of the default paging text (defaults to "Refresh")
22206      * @type String
22207      */
22208     refreshText : "Refresh",
22209
22210     buttons : false,
22211     // private
22212     onRender : function(ct, position) 
22213     {
22214         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22215         this.navgroup.parentId = this.id;
22216         this.navgroup.onRender(this.el, null);
22217         // add the buttons to the navgroup
22218         
22219         if(this.displayInfo){
22220             Roo.log(this.el.select('ul.navbar-nav',true).first());
22221             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22222             this.displayEl = this.el.select('.x-paging-info', true).first();
22223 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22224 //            this.displayEl = navel.el.select('span',true).first();
22225         }
22226         
22227         var _this = this;
22228         
22229         if(this.buttons){
22230             Roo.each(_this.buttons, function(e){ // this might need to use render????
22231                Roo.factory(e).onRender(_this.el, null);
22232             });
22233         }
22234             
22235         Roo.each(_this.toolbarItems, function(e) {
22236             _this.navgroup.addItem(e);
22237         });
22238         
22239         
22240         this.first = this.navgroup.addItem({
22241             tooltip: this.firstText,
22242             cls: "prev",
22243             icon : 'fa fa-backward',
22244             disabled: true,
22245             preventDefault: true,
22246             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22247         });
22248         
22249         this.prev =  this.navgroup.addItem({
22250             tooltip: this.prevText,
22251             cls: "prev",
22252             icon : 'fa fa-step-backward',
22253             disabled: true,
22254             preventDefault: true,
22255             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22256         });
22257     //this.addSeparator();
22258         
22259         
22260         var field = this.navgroup.addItem( {
22261             tagtype : 'span',
22262             cls : 'x-paging-position',
22263             
22264             html : this.beforePageText  +
22265                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22266                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22267          } ); //?? escaped?
22268         
22269         this.field = field.el.select('input', true).first();
22270         this.field.on("keydown", this.onPagingKeydown, this);
22271         this.field.on("focus", function(){this.dom.select();});
22272     
22273     
22274         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22275         //this.field.setHeight(18);
22276         //this.addSeparator();
22277         this.next = this.navgroup.addItem({
22278             tooltip: this.nextText,
22279             cls: "next",
22280             html : ' <i class="fa fa-step-forward">',
22281             disabled: true,
22282             preventDefault: true,
22283             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22284         });
22285         this.last = this.navgroup.addItem({
22286             tooltip: this.lastText,
22287             icon : 'fa fa-forward',
22288             cls: "next",
22289             disabled: true,
22290             preventDefault: true,
22291             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22292         });
22293     //this.addSeparator();
22294         this.loading = this.navgroup.addItem({
22295             tooltip: this.refreshText,
22296             icon: 'fa fa-refresh',
22297             preventDefault: true,
22298             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22299         });
22300         
22301     },
22302
22303     // private
22304     updateInfo : function(){
22305         if(this.displayEl){
22306             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22307             var msg = count == 0 ?
22308                 this.emptyMsg :
22309                 String.format(
22310                     this.displayMsg,
22311                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22312                 );
22313             this.displayEl.update(msg);
22314         }
22315     },
22316
22317     // private
22318     onLoad : function(ds, r, o){
22319        this.cursor = o.params ? o.params.start : 0;
22320        var d = this.getPageData(),
22321             ap = d.activePage,
22322             ps = d.pages;
22323         
22324        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22325        this.field.dom.value = ap;
22326        this.first.setDisabled(ap == 1);
22327        this.prev.setDisabled(ap == 1);
22328        this.next.setDisabled(ap == ps);
22329        this.last.setDisabled(ap == ps);
22330        this.loading.enable();
22331        this.updateInfo();
22332     },
22333
22334     // private
22335     getPageData : function(){
22336         var total = this.ds.getTotalCount();
22337         return {
22338             total : total,
22339             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22340             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22341         };
22342     },
22343
22344     // private
22345     onLoadError : function(){
22346         this.loading.enable();
22347     },
22348
22349     // private
22350     onPagingKeydown : function(e){
22351         var k = e.getKey();
22352         var d = this.getPageData();
22353         if(k == e.RETURN){
22354             var v = this.field.dom.value, pageNum;
22355             if(!v || isNaN(pageNum = parseInt(v, 10))){
22356                 this.field.dom.value = d.activePage;
22357                 return;
22358             }
22359             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22360             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22361             e.stopEvent();
22362         }
22363         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))
22364         {
22365           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22366           this.field.dom.value = pageNum;
22367           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22368           e.stopEvent();
22369         }
22370         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22371         {
22372           var v = this.field.dom.value, pageNum; 
22373           var increment = (e.shiftKey) ? 10 : 1;
22374           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22375                 increment *= -1;
22376           }
22377           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22378             this.field.dom.value = d.activePage;
22379             return;
22380           }
22381           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22382           {
22383             this.field.dom.value = parseInt(v, 10) + increment;
22384             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22385             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22386           }
22387           e.stopEvent();
22388         }
22389     },
22390
22391     // private
22392     beforeLoad : function(){
22393         if(this.loading){
22394             this.loading.disable();
22395         }
22396     },
22397
22398     // private
22399     onClick : function(which){
22400         
22401         var ds = this.ds;
22402         if (!ds) {
22403             return;
22404         }
22405         
22406         switch(which){
22407             case "first":
22408                 ds.load({params:{start: 0, limit: this.pageSize}});
22409             break;
22410             case "prev":
22411                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22412             break;
22413             case "next":
22414                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22415             break;
22416             case "last":
22417                 var total = ds.getTotalCount();
22418                 var extra = total % this.pageSize;
22419                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22420                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22421             break;
22422             case "refresh":
22423                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22424             break;
22425         }
22426     },
22427
22428     /**
22429      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22430      * @param {Roo.data.Store} store The data store to unbind
22431      */
22432     unbind : function(ds){
22433         ds.un("beforeload", this.beforeLoad, this);
22434         ds.un("load", this.onLoad, this);
22435         ds.un("loadexception", this.onLoadError, this);
22436         ds.un("remove", this.updateInfo, this);
22437         ds.un("add", this.updateInfo, this);
22438         this.ds = undefined;
22439     },
22440
22441     /**
22442      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22443      * @param {Roo.data.Store} store The data store to bind
22444      */
22445     bind : function(ds){
22446         ds.on("beforeload", this.beforeLoad, this);
22447         ds.on("load", this.onLoad, this);
22448         ds.on("loadexception", this.onLoadError, this);
22449         ds.on("remove", this.updateInfo, this);
22450         ds.on("add", this.updateInfo, this);
22451         this.ds = ds;
22452     }
22453 });/*
22454  * - LGPL
22455  *
22456  * element
22457  * 
22458  */
22459
22460 /**
22461  * @class Roo.bootstrap.MessageBar
22462  * @extends Roo.bootstrap.Component
22463  * Bootstrap MessageBar class
22464  * @cfg {String} html contents of the MessageBar
22465  * @cfg {String} weight (info | success | warning | danger) default info
22466  * @cfg {String} beforeClass insert the bar before the given class
22467  * @cfg {Boolean} closable (true | false) default false
22468  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22469  * 
22470  * @constructor
22471  * Create a new Element
22472  * @param {Object} config The config object
22473  */
22474
22475 Roo.bootstrap.MessageBar = function(config){
22476     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22477 };
22478
22479 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22480     
22481     html: '',
22482     weight: 'info',
22483     closable: false,
22484     fixed: false,
22485     beforeClass: 'bootstrap-sticky-wrap',
22486     
22487     getAutoCreate : function(){
22488         
22489         var cfg = {
22490             tag: 'div',
22491             cls: 'alert alert-dismissable alert-' + this.weight,
22492             cn: [
22493                 {
22494                     tag: 'span',
22495                     cls: 'message',
22496                     html: this.html || ''
22497                 }
22498             ]
22499         };
22500         
22501         if(this.fixed){
22502             cfg.cls += ' alert-messages-fixed';
22503         }
22504         
22505         if(this.closable){
22506             cfg.cn.push({
22507                 tag: 'button',
22508                 cls: 'close',
22509                 html: 'x'
22510             });
22511         }
22512         
22513         return cfg;
22514     },
22515     
22516     onRender : function(ct, position)
22517     {
22518         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22519         
22520         if(!this.el){
22521             var cfg = Roo.apply({},  this.getAutoCreate());
22522             cfg.id = Roo.id();
22523             
22524             if (this.cls) {
22525                 cfg.cls += ' ' + this.cls;
22526             }
22527             if (this.style) {
22528                 cfg.style = this.style;
22529             }
22530             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22531             
22532             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22533         }
22534         
22535         this.el.select('>button.close').on('click', this.hide, this);
22536         
22537     },
22538     
22539     show : function()
22540     {
22541         if (!this.rendered) {
22542             this.render();
22543         }
22544         
22545         this.el.show();
22546         
22547         this.fireEvent('show', this);
22548         
22549     },
22550     
22551     hide : function()
22552     {
22553         if (!this.rendered) {
22554             this.render();
22555         }
22556         
22557         this.el.hide();
22558         
22559         this.fireEvent('hide', this);
22560     },
22561     
22562     update : function()
22563     {
22564 //        var e = this.el.dom.firstChild;
22565 //        
22566 //        if(this.closable){
22567 //            e = e.nextSibling;
22568 //        }
22569 //        
22570 //        e.data = this.html || '';
22571
22572         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22573     }
22574    
22575 });
22576
22577  
22578
22579      /*
22580  * - LGPL
22581  *
22582  * Graph
22583  * 
22584  */
22585
22586
22587 /**
22588  * @class Roo.bootstrap.Graph
22589  * @extends Roo.bootstrap.Component
22590  * Bootstrap Graph class
22591 > Prameters
22592  -sm {number} sm 4
22593  -md {number} md 5
22594  @cfg {String} graphtype  bar | vbar | pie
22595  @cfg {number} g_x coodinator | centre x (pie)
22596  @cfg {number} g_y coodinator | centre y (pie)
22597  @cfg {number} g_r radius (pie)
22598  @cfg {number} g_height height of the chart (respected by all elements in the set)
22599  @cfg {number} g_width width of the chart (respected by all elements in the set)
22600  @cfg {Object} title The title of the chart
22601     
22602  -{Array}  values
22603  -opts (object) options for the chart 
22604      o {
22605      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22606      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22607      o vgutter (number)
22608      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.
22609      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22610      o to
22611      o stretch (boolean)
22612      o }
22613  -opts (object) options for the pie
22614      o{
22615      o cut
22616      o startAngle (number)
22617      o endAngle (number)
22618      } 
22619  *
22620  * @constructor
22621  * Create a new Input
22622  * @param {Object} config The config object
22623  */
22624
22625 Roo.bootstrap.Graph = function(config){
22626     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22627     
22628     this.addEvents({
22629         // img events
22630         /**
22631          * @event click
22632          * The img click event for the img.
22633          * @param {Roo.EventObject} e
22634          */
22635         "click" : true
22636     });
22637 };
22638
22639 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22640     
22641     sm: 4,
22642     md: 5,
22643     graphtype: 'bar',
22644     g_height: 250,
22645     g_width: 400,
22646     g_x: 50,
22647     g_y: 50,
22648     g_r: 30,
22649     opts:{
22650         //g_colors: this.colors,
22651         g_type: 'soft',
22652         g_gutter: '20%'
22653
22654     },
22655     title : false,
22656
22657     getAutoCreate : function(){
22658         
22659         var cfg = {
22660             tag: 'div',
22661             html : null
22662         };
22663         
22664         
22665         return  cfg;
22666     },
22667
22668     onRender : function(ct,position){
22669         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22670         this.raphael = Raphael(this.el.dom);
22671         
22672                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22673                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22674                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22675                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22676                 /*
22677                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22678                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22679                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22680                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22681                 
22682                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22683                 r.barchart(330, 10, 300, 220, data1);
22684                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22685                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22686                 */
22687                 
22688                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22689                 // r.barchart(30, 30, 560, 250,  xdata, {
22690                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22691                 //     axis : "0 0 1 1",
22692                 //     axisxlabels :  xdata
22693                 //     //yvalues : cols,
22694                    
22695                 // });
22696 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22697 //        
22698 //        this.load(null,xdata,{
22699 //                axis : "0 0 1 1",
22700 //                axisxlabels :  xdata
22701 //                });
22702
22703     },
22704
22705     load : function(graphtype,xdata,opts){
22706         this.raphael.clear();
22707         if(!graphtype) {
22708             graphtype = this.graphtype;
22709         }
22710         if(!opts){
22711             opts = this.opts;
22712         }
22713         var r = this.raphael,
22714             fin = function () {
22715                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22716             },
22717             fout = function () {
22718                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22719             },
22720             pfin = function() {
22721                 this.sector.stop();
22722                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22723
22724                 if (this.label) {
22725                     this.label[0].stop();
22726                     this.label[0].attr({ r: 7.5 });
22727                     this.label[1].attr({ "font-weight": 800 });
22728                 }
22729             },
22730             pfout = function() {
22731                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22732
22733                 if (this.label) {
22734                     this.label[0].animate({ r: 5 }, 500, "bounce");
22735                     this.label[1].attr({ "font-weight": 400 });
22736                 }
22737             };
22738
22739         switch(graphtype){
22740             case 'bar':
22741                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22742                 break;
22743             case 'hbar':
22744                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22745                 break;
22746             case 'pie':
22747 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22748 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22749 //            
22750                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22751                 
22752                 break;
22753
22754         }
22755         
22756         if(this.title){
22757             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22758         }
22759         
22760     },
22761     
22762     setTitle: function(o)
22763     {
22764         this.title = o;
22765     },
22766     
22767     initEvents: function() {
22768         
22769         if(!this.href){
22770             this.el.on('click', this.onClick, this);
22771         }
22772     },
22773     
22774     onClick : function(e)
22775     {
22776         Roo.log('img onclick');
22777         this.fireEvent('click', this, e);
22778     }
22779    
22780 });
22781
22782  
22783 /*
22784  * - LGPL
22785  *
22786  * numberBox
22787  * 
22788  */
22789 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22790
22791 /**
22792  * @class Roo.bootstrap.dash.NumberBox
22793  * @extends Roo.bootstrap.Component
22794  * Bootstrap NumberBox class
22795  * @cfg {String} headline Box headline
22796  * @cfg {String} content Box content
22797  * @cfg {String} icon Box icon
22798  * @cfg {String} footer Footer text
22799  * @cfg {String} fhref Footer href
22800  * 
22801  * @constructor
22802  * Create a new NumberBox
22803  * @param {Object} config The config object
22804  */
22805
22806
22807 Roo.bootstrap.dash.NumberBox = function(config){
22808     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22809     
22810 };
22811
22812 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22813     
22814     headline : '',
22815     content : '',
22816     icon : '',
22817     footer : '',
22818     fhref : '',
22819     ficon : '',
22820     
22821     getAutoCreate : function(){
22822         
22823         var cfg = {
22824             tag : 'div',
22825             cls : 'small-box ',
22826             cn : [
22827                 {
22828                     tag : 'div',
22829                     cls : 'inner',
22830                     cn :[
22831                         {
22832                             tag : 'h3',
22833                             cls : 'roo-headline',
22834                             html : this.headline
22835                         },
22836                         {
22837                             tag : 'p',
22838                             cls : 'roo-content',
22839                             html : this.content
22840                         }
22841                     ]
22842                 }
22843             ]
22844         };
22845         
22846         if(this.icon){
22847             cfg.cn.push({
22848                 tag : 'div',
22849                 cls : 'icon',
22850                 cn :[
22851                     {
22852                         tag : 'i',
22853                         cls : 'ion ' + this.icon
22854                     }
22855                 ]
22856             });
22857         }
22858         
22859         if(this.footer){
22860             var footer = {
22861                 tag : 'a',
22862                 cls : 'small-box-footer',
22863                 href : this.fhref || '#',
22864                 html : this.footer
22865             };
22866             
22867             cfg.cn.push(footer);
22868             
22869         }
22870         
22871         return  cfg;
22872     },
22873
22874     onRender : function(ct,position){
22875         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22876
22877
22878        
22879                 
22880     },
22881
22882     setHeadline: function (value)
22883     {
22884         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22885     },
22886     
22887     setFooter: function (value, href)
22888     {
22889         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22890         
22891         if(href){
22892             this.el.select('a.small-box-footer',true).first().attr('href', href);
22893         }
22894         
22895     },
22896
22897     setContent: function (value)
22898     {
22899         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22900     },
22901
22902     initEvents: function() 
22903     {   
22904         
22905     }
22906     
22907 });
22908
22909  
22910 /*
22911  * - LGPL
22912  *
22913  * TabBox
22914  * 
22915  */
22916 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22917
22918 /**
22919  * @class Roo.bootstrap.dash.TabBox
22920  * @extends Roo.bootstrap.Component
22921  * Bootstrap TabBox class
22922  * @cfg {String} title Title of the TabBox
22923  * @cfg {String} icon Icon of the TabBox
22924  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22925  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22926  * 
22927  * @constructor
22928  * Create a new TabBox
22929  * @param {Object} config The config object
22930  */
22931
22932
22933 Roo.bootstrap.dash.TabBox = function(config){
22934     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22935     this.addEvents({
22936         // raw events
22937         /**
22938          * @event addpane
22939          * When a pane is added
22940          * @param {Roo.bootstrap.dash.TabPane} pane
22941          */
22942         "addpane" : true,
22943         /**
22944          * @event activatepane
22945          * When a pane is activated
22946          * @param {Roo.bootstrap.dash.TabPane} pane
22947          */
22948         "activatepane" : true
22949         
22950          
22951     });
22952     
22953     this.panes = [];
22954 };
22955
22956 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22957
22958     title : '',
22959     icon : false,
22960     showtabs : true,
22961     tabScrollable : false,
22962     
22963     getChildContainer : function()
22964     {
22965         return this.el.select('.tab-content', true).first();
22966     },
22967     
22968     getAutoCreate : function(){
22969         
22970         var header = {
22971             tag: 'li',
22972             cls: 'pull-left header',
22973             html: this.title,
22974             cn : []
22975         };
22976         
22977         if(this.icon){
22978             header.cn.push({
22979                 tag: 'i',
22980                 cls: 'fa ' + this.icon
22981             });
22982         }
22983         
22984         var h = {
22985             tag: 'ul',
22986             cls: 'nav nav-tabs pull-right',
22987             cn: [
22988                 header
22989             ]
22990         };
22991         
22992         if(this.tabScrollable){
22993             h = {
22994                 tag: 'div',
22995                 cls: 'tab-header',
22996                 cn: [
22997                     {
22998                         tag: 'ul',
22999                         cls: 'nav nav-tabs pull-right',
23000                         cn: [
23001                             header
23002                         ]
23003                     }
23004                 ]
23005             };
23006         }
23007         
23008         var cfg = {
23009             tag: 'div',
23010             cls: 'nav-tabs-custom',
23011             cn: [
23012                 h,
23013                 {
23014                     tag: 'div',
23015                     cls: 'tab-content no-padding',
23016                     cn: []
23017                 }
23018             ]
23019         };
23020
23021         return  cfg;
23022     },
23023     initEvents : function()
23024     {
23025         //Roo.log('add add pane handler');
23026         this.on('addpane', this.onAddPane, this);
23027     },
23028      /**
23029      * Updates the box title
23030      * @param {String} html to set the title to.
23031      */
23032     setTitle : function(value)
23033     {
23034         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23035     },
23036     onAddPane : function(pane)
23037     {
23038         this.panes.push(pane);
23039         //Roo.log('addpane');
23040         //Roo.log(pane);
23041         // tabs are rendere left to right..
23042         if(!this.showtabs){
23043             return;
23044         }
23045         
23046         var ctr = this.el.select('.nav-tabs', true).first();
23047          
23048          
23049         var existing = ctr.select('.nav-tab',true);
23050         var qty = existing.getCount();;
23051         
23052         
23053         var tab = ctr.createChild({
23054             tag : 'li',
23055             cls : 'nav-tab' + (qty ? '' : ' active'),
23056             cn : [
23057                 {
23058                     tag : 'a',
23059                     href:'#',
23060                     html : pane.title
23061                 }
23062             ]
23063         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23064         pane.tab = tab;
23065         
23066         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23067         if (!qty) {
23068             pane.el.addClass('active');
23069         }
23070         
23071                 
23072     },
23073     onTabClick : function(ev,un,ob,pane)
23074     {
23075         //Roo.log('tab - prev default');
23076         ev.preventDefault();
23077         
23078         
23079         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23080         pane.tab.addClass('active');
23081         //Roo.log(pane.title);
23082         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23083         // technically we should have a deactivate event.. but maybe add later.
23084         // and it should not de-activate the selected tab...
23085         this.fireEvent('activatepane', pane);
23086         pane.el.addClass('active');
23087         pane.fireEvent('activate');
23088         
23089         
23090     },
23091     
23092     getActivePane : function()
23093     {
23094         var r = false;
23095         Roo.each(this.panes, function(p) {
23096             if(p.el.hasClass('active')){
23097                 r = p;
23098                 return false;
23099             }
23100             
23101             return;
23102         });
23103         
23104         return r;
23105     }
23106     
23107     
23108 });
23109
23110  
23111 /*
23112  * - LGPL
23113  *
23114  * Tab pane
23115  * 
23116  */
23117 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23118 /**
23119  * @class Roo.bootstrap.TabPane
23120  * @extends Roo.bootstrap.Component
23121  * Bootstrap TabPane class
23122  * @cfg {Boolean} active (false | true) Default false
23123  * @cfg {String} title title of panel
23124
23125  * 
23126  * @constructor
23127  * Create a new TabPane
23128  * @param {Object} config The config object
23129  */
23130
23131 Roo.bootstrap.dash.TabPane = function(config){
23132     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23133     
23134     this.addEvents({
23135         // raw events
23136         /**
23137          * @event activate
23138          * When a pane is activated
23139          * @param {Roo.bootstrap.dash.TabPane} pane
23140          */
23141         "activate" : true
23142          
23143     });
23144 };
23145
23146 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23147     
23148     active : false,
23149     title : '',
23150     
23151     // the tabBox that this is attached to.
23152     tab : false,
23153      
23154     getAutoCreate : function() 
23155     {
23156         var cfg = {
23157             tag: 'div',
23158             cls: 'tab-pane'
23159         };
23160         
23161         if(this.active){
23162             cfg.cls += ' active';
23163         }
23164         
23165         return cfg;
23166     },
23167     initEvents  : function()
23168     {
23169         //Roo.log('trigger add pane handler');
23170         this.parent().fireEvent('addpane', this)
23171     },
23172     
23173      /**
23174      * Updates the tab title 
23175      * @param {String} html to set the title to.
23176      */
23177     setTitle: function(str)
23178     {
23179         if (!this.tab) {
23180             return;
23181         }
23182         this.title = str;
23183         this.tab.select('a', true).first().dom.innerHTML = str;
23184         
23185     }
23186     
23187     
23188     
23189 });
23190
23191  
23192
23193
23194  /*
23195  * - LGPL
23196  *
23197  * menu
23198  * 
23199  */
23200 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23201
23202 /**
23203  * @class Roo.bootstrap.menu.Menu
23204  * @extends Roo.bootstrap.Component
23205  * Bootstrap Menu class - container for Menu
23206  * @cfg {String} html Text of the menu
23207  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23208  * @cfg {String} icon Font awesome icon
23209  * @cfg {String} pos Menu align to (top | bottom) default bottom
23210  * 
23211  * 
23212  * @constructor
23213  * Create a new Menu
23214  * @param {Object} config The config object
23215  */
23216
23217
23218 Roo.bootstrap.menu.Menu = function(config){
23219     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23220     
23221     this.addEvents({
23222         /**
23223          * @event beforeshow
23224          * Fires before this menu is displayed
23225          * @param {Roo.bootstrap.menu.Menu} this
23226          */
23227         beforeshow : true,
23228         /**
23229          * @event beforehide
23230          * Fires before this menu is hidden
23231          * @param {Roo.bootstrap.menu.Menu} this
23232          */
23233         beforehide : true,
23234         /**
23235          * @event show
23236          * Fires after this menu is displayed
23237          * @param {Roo.bootstrap.menu.Menu} this
23238          */
23239         show : true,
23240         /**
23241          * @event hide
23242          * Fires after this menu is hidden
23243          * @param {Roo.bootstrap.menu.Menu} this
23244          */
23245         hide : true,
23246         /**
23247          * @event click
23248          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23249          * @param {Roo.bootstrap.menu.Menu} this
23250          * @param {Roo.EventObject} e
23251          */
23252         click : true
23253     });
23254     
23255 };
23256
23257 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23258     
23259     submenu : false,
23260     html : '',
23261     weight : 'default',
23262     icon : false,
23263     pos : 'bottom',
23264     
23265     
23266     getChildContainer : function() {
23267         if(this.isSubMenu){
23268             return this.el;
23269         }
23270         
23271         return this.el.select('ul.dropdown-menu', true).first();  
23272     },
23273     
23274     getAutoCreate : function()
23275     {
23276         var text = [
23277             {
23278                 tag : 'span',
23279                 cls : 'roo-menu-text',
23280                 html : this.html
23281             }
23282         ];
23283         
23284         if(this.icon){
23285             text.unshift({
23286                 tag : 'i',
23287                 cls : 'fa ' + this.icon
23288             })
23289         }
23290         
23291         
23292         var cfg = {
23293             tag : 'div',
23294             cls : 'btn-group',
23295             cn : [
23296                 {
23297                     tag : 'button',
23298                     cls : 'dropdown-button btn btn-' + this.weight,
23299                     cn : text
23300                 },
23301                 {
23302                     tag : 'button',
23303                     cls : 'dropdown-toggle btn btn-' + this.weight,
23304                     cn : [
23305                         {
23306                             tag : 'span',
23307                             cls : 'caret'
23308                         }
23309                     ]
23310                 },
23311                 {
23312                     tag : 'ul',
23313                     cls : 'dropdown-menu'
23314                 }
23315             ]
23316             
23317         };
23318         
23319         if(this.pos == 'top'){
23320             cfg.cls += ' dropup';
23321         }
23322         
23323         if(this.isSubMenu){
23324             cfg = {
23325                 tag : 'ul',
23326                 cls : 'dropdown-menu'
23327             }
23328         }
23329         
23330         return cfg;
23331     },
23332     
23333     onRender : function(ct, position)
23334     {
23335         this.isSubMenu = ct.hasClass('dropdown-submenu');
23336         
23337         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23338     },
23339     
23340     initEvents : function() 
23341     {
23342         if(this.isSubMenu){
23343             return;
23344         }
23345         
23346         this.hidden = true;
23347         
23348         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23349         this.triggerEl.on('click', this.onTriggerPress, this);
23350         
23351         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23352         this.buttonEl.on('click', this.onClick, this);
23353         
23354     },
23355     
23356     list : function()
23357     {
23358         if(this.isSubMenu){
23359             return this.el;
23360         }
23361         
23362         return this.el.select('ul.dropdown-menu', true).first();
23363     },
23364     
23365     onClick : function(e)
23366     {
23367         this.fireEvent("click", this, e);
23368     },
23369     
23370     onTriggerPress  : function(e)
23371     {   
23372         if (this.isVisible()) {
23373             this.hide();
23374         } else {
23375             this.show();
23376         }
23377     },
23378     
23379     isVisible : function(){
23380         return !this.hidden;
23381     },
23382     
23383     show : function()
23384     {
23385         this.fireEvent("beforeshow", this);
23386         
23387         this.hidden = false;
23388         this.el.addClass('open');
23389         
23390         Roo.get(document).on("mouseup", this.onMouseUp, this);
23391         
23392         this.fireEvent("show", this);
23393         
23394         
23395     },
23396     
23397     hide : function()
23398     {
23399         this.fireEvent("beforehide", this);
23400         
23401         this.hidden = true;
23402         this.el.removeClass('open');
23403         
23404         Roo.get(document).un("mouseup", this.onMouseUp);
23405         
23406         this.fireEvent("hide", this);
23407     },
23408     
23409     onMouseUp : function()
23410     {
23411         this.hide();
23412     }
23413     
23414 });
23415
23416  
23417  /*
23418  * - LGPL
23419  *
23420  * menu item
23421  * 
23422  */
23423 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23424
23425 /**
23426  * @class Roo.bootstrap.menu.Item
23427  * @extends Roo.bootstrap.Component
23428  * Bootstrap MenuItem class
23429  * @cfg {Boolean} submenu (true | false) default false
23430  * @cfg {String} html text of the item
23431  * @cfg {String} href the link
23432  * @cfg {Boolean} disable (true | false) default false
23433  * @cfg {Boolean} preventDefault (true | false) default true
23434  * @cfg {String} icon Font awesome icon
23435  * @cfg {String} pos Submenu align to (left | right) default right 
23436  * 
23437  * 
23438  * @constructor
23439  * Create a new Item
23440  * @param {Object} config The config object
23441  */
23442
23443
23444 Roo.bootstrap.menu.Item = function(config){
23445     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23446     this.addEvents({
23447         /**
23448          * @event mouseover
23449          * Fires when the mouse is hovering over this menu
23450          * @param {Roo.bootstrap.menu.Item} this
23451          * @param {Roo.EventObject} e
23452          */
23453         mouseover : true,
23454         /**
23455          * @event mouseout
23456          * Fires when the mouse exits this menu
23457          * @param {Roo.bootstrap.menu.Item} this
23458          * @param {Roo.EventObject} e
23459          */
23460         mouseout : true,
23461         // raw events
23462         /**
23463          * @event click
23464          * The raw click event for the entire grid.
23465          * @param {Roo.EventObject} e
23466          */
23467         click : true
23468     });
23469 };
23470
23471 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23472     
23473     submenu : false,
23474     href : '',
23475     html : '',
23476     preventDefault: true,
23477     disable : false,
23478     icon : false,
23479     pos : 'right',
23480     
23481     getAutoCreate : function()
23482     {
23483         var text = [
23484             {
23485                 tag : 'span',
23486                 cls : 'roo-menu-item-text',
23487                 html : this.html
23488             }
23489         ];
23490         
23491         if(this.icon){
23492             text.unshift({
23493                 tag : 'i',
23494                 cls : 'fa ' + this.icon
23495             })
23496         }
23497         
23498         var cfg = {
23499             tag : 'li',
23500             cn : [
23501                 {
23502                     tag : 'a',
23503                     href : this.href || '#',
23504                     cn : text
23505                 }
23506             ]
23507         };
23508         
23509         if(this.disable){
23510             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23511         }
23512         
23513         if(this.submenu){
23514             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23515             
23516             if(this.pos == 'left'){
23517                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23518             }
23519         }
23520         
23521         return cfg;
23522     },
23523     
23524     initEvents : function() 
23525     {
23526         this.el.on('mouseover', this.onMouseOver, this);
23527         this.el.on('mouseout', this.onMouseOut, this);
23528         
23529         this.el.select('a', true).first().on('click', this.onClick, this);
23530         
23531     },
23532     
23533     onClick : function(e)
23534     {
23535         if(this.preventDefault){
23536             e.preventDefault();
23537         }
23538         
23539         this.fireEvent("click", this, e);
23540     },
23541     
23542     onMouseOver : function(e)
23543     {
23544         if(this.submenu && this.pos == 'left'){
23545             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23546         }
23547         
23548         this.fireEvent("mouseover", this, e);
23549     },
23550     
23551     onMouseOut : function(e)
23552     {
23553         this.fireEvent("mouseout", this, e);
23554     }
23555 });
23556
23557  
23558
23559  /*
23560  * - LGPL
23561  *
23562  * menu separator
23563  * 
23564  */
23565 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23566
23567 /**
23568  * @class Roo.bootstrap.menu.Separator
23569  * @extends Roo.bootstrap.Component
23570  * Bootstrap Separator class
23571  * 
23572  * @constructor
23573  * Create a new Separator
23574  * @param {Object} config The config object
23575  */
23576
23577
23578 Roo.bootstrap.menu.Separator = function(config){
23579     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23580 };
23581
23582 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23583     
23584     getAutoCreate : function(){
23585         var cfg = {
23586             tag : 'li',
23587             cls: 'divider'
23588         };
23589         
23590         return cfg;
23591     }
23592    
23593 });
23594
23595  
23596
23597  /*
23598  * - LGPL
23599  *
23600  * Tooltip
23601  * 
23602  */
23603
23604 /**
23605  * @class Roo.bootstrap.Tooltip
23606  * Bootstrap Tooltip class
23607  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23608  * to determine which dom element triggers the tooltip.
23609  * 
23610  * It needs to add support for additional attributes like tooltip-position
23611  * 
23612  * @constructor
23613  * Create a new Toolti
23614  * @param {Object} config The config object
23615  */
23616
23617 Roo.bootstrap.Tooltip = function(config){
23618     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23619 };
23620
23621 Roo.apply(Roo.bootstrap.Tooltip, {
23622     /**
23623      * @function init initialize tooltip monitoring.
23624      * @static
23625      */
23626     currentEl : false,
23627     currentTip : false,
23628     currentRegion : false,
23629     
23630     //  init : delay?
23631     
23632     init : function()
23633     {
23634         Roo.get(document).on('mouseover', this.enter ,this);
23635         Roo.get(document).on('mouseout', this.leave, this);
23636          
23637         
23638         this.currentTip = new Roo.bootstrap.Tooltip();
23639     },
23640     
23641     enter : function(ev)
23642     {
23643         var dom = ev.getTarget();
23644         
23645         //Roo.log(['enter',dom]);
23646         var el = Roo.fly(dom);
23647         if (this.currentEl) {
23648             //Roo.log(dom);
23649             //Roo.log(this.currentEl);
23650             //Roo.log(this.currentEl.contains(dom));
23651             if (this.currentEl == el) {
23652                 return;
23653             }
23654             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23655                 return;
23656             }
23657
23658         }
23659         
23660         if (this.currentTip.el) {
23661             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23662         }    
23663         //Roo.log(ev);
23664         var bindEl = el;
23665         
23666         // you can not look for children, as if el is the body.. then everythign is the child..
23667         if (!el.attr('tooltip')) { //
23668             if (!el.select("[tooltip]").elements.length) {
23669                 return;
23670             }
23671             // is the mouse over this child...?
23672             bindEl = el.select("[tooltip]").first();
23673             var xy = ev.getXY();
23674             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23675                 //Roo.log("not in region.");
23676                 return;
23677             }
23678             //Roo.log("child element over..");
23679             
23680         }
23681         this.currentEl = bindEl;
23682         this.currentTip.bind(bindEl);
23683         this.currentRegion = Roo.lib.Region.getRegion(dom);
23684         this.currentTip.enter();
23685         
23686     },
23687     leave : function(ev)
23688     {
23689         var dom = ev.getTarget();
23690         //Roo.log(['leave',dom]);
23691         if (!this.currentEl) {
23692             return;
23693         }
23694         
23695         
23696         if (dom != this.currentEl.dom) {
23697             return;
23698         }
23699         var xy = ev.getXY();
23700         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23701             return;
23702         }
23703         // only activate leave if mouse cursor is outside... bounding box..
23704         
23705         
23706         
23707         
23708         if (this.currentTip) {
23709             this.currentTip.leave();
23710         }
23711         //Roo.log('clear currentEl');
23712         this.currentEl = false;
23713         
23714         
23715     },
23716     alignment : {
23717         'left' : ['r-l', [-2,0], 'right'],
23718         'right' : ['l-r', [2,0], 'left'],
23719         'bottom' : ['t-b', [0,2], 'top'],
23720         'top' : [ 'b-t', [0,-2], 'bottom']
23721     }
23722     
23723 });
23724
23725
23726 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23727     
23728     
23729     bindEl : false,
23730     
23731     delay : null, // can be { show : 300 , hide: 500}
23732     
23733     timeout : null,
23734     
23735     hoverState : null, //???
23736     
23737     placement : 'bottom', 
23738     
23739     getAutoCreate : function(){
23740     
23741         var cfg = {
23742            cls : 'tooltip',
23743            role : 'tooltip',
23744            cn : [
23745                 {
23746                     cls : 'tooltip-arrow'
23747                 },
23748                 {
23749                     cls : 'tooltip-inner'
23750                 }
23751            ]
23752         };
23753         
23754         return cfg;
23755     },
23756     bind : function(el)
23757     {
23758         this.bindEl = el;
23759     },
23760       
23761     
23762     enter : function () {
23763        
23764         if (this.timeout != null) {
23765             clearTimeout(this.timeout);
23766         }
23767         
23768         this.hoverState = 'in';
23769          //Roo.log("enter - show");
23770         if (!this.delay || !this.delay.show) {
23771             this.show();
23772             return;
23773         }
23774         var _t = this;
23775         this.timeout = setTimeout(function () {
23776             if (_t.hoverState == 'in') {
23777                 _t.show();
23778             }
23779         }, this.delay.show);
23780     },
23781     leave : function()
23782     {
23783         clearTimeout(this.timeout);
23784     
23785         this.hoverState = 'out';
23786          if (!this.delay || !this.delay.hide) {
23787             this.hide();
23788             return;
23789         }
23790        
23791         var _t = this;
23792         this.timeout = setTimeout(function () {
23793             //Roo.log("leave - timeout");
23794             
23795             if (_t.hoverState == 'out') {
23796                 _t.hide();
23797                 Roo.bootstrap.Tooltip.currentEl = false;
23798             }
23799         }, delay);
23800     },
23801     
23802     show : function ()
23803     {
23804         if (!this.el) {
23805             this.render(document.body);
23806         }
23807         // set content.
23808         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23809         
23810         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23811         
23812         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23813         
23814         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23815         
23816         var placement = typeof this.placement == 'function' ?
23817             this.placement.call(this, this.el, on_el) :
23818             this.placement;
23819             
23820         var autoToken = /\s?auto?\s?/i;
23821         var autoPlace = autoToken.test(placement);
23822         if (autoPlace) {
23823             placement = placement.replace(autoToken, '') || 'top';
23824         }
23825         
23826         //this.el.detach()
23827         //this.el.setXY([0,0]);
23828         this.el.show();
23829         //this.el.dom.style.display='block';
23830         
23831         //this.el.appendTo(on_el);
23832         
23833         var p = this.getPosition();
23834         var box = this.el.getBox();
23835         
23836         if (autoPlace) {
23837             // fixme..
23838         }
23839         
23840         var align = Roo.bootstrap.Tooltip.alignment[placement];
23841         
23842         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23843         
23844         if(placement == 'top' || placement == 'bottom'){
23845             if(xy[0] < 0){
23846                 placement = 'right';
23847             }
23848             
23849             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23850                 placement = 'left';
23851             }
23852         }
23853         
23854         align = Roo.bootstrap.Tooltip.alignment[placement];
23855         
23856         this.el.alignTo(this.bindEl, align[0],align[1]);
23857         //var arrow = this.el.select('.arrow',true).first();
23858         //arrow.set(align[2], 
23859         
23860         this.el.addClass(placement);
23861         
23862         this.el.addClass('in fade');
23863         
23864         this.hoverState = null;
23865         
23866         if (this.el.hasClass('fade')) {
23867             // fade it?
23868         }
23869         
23870     },
23871     hide : function()
23872     {
23873          
23874         if (!this.el) {
23875             return;
23876         }
23877         //this.el.setXY([0,0]);
23878         this.el.removeClass('in');
23879         //this.el.hide();
23880         
23881     }
23882     
23883 });
23884  
23885
23886  /*
23887  * - LGPL
23888  *
23889  * Location Picker
23890  * 
23891  */
23892
23893 /**
23894  * @class Roo.bootstrap.LocationPicker
23895  * @extends Roo.bootstrap.Component
23896  * Bootstrap LocationPicker class
23897  * @cfg {Number} latitude Position when init default 0
23898  * @cfg {Number} longitude Position when init default 0
23899  * @cfg {Number} zoom default 15
23900  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23901  * @cfg {Boolean} mapTypeControl default false
23902  * @cfg {Boolean} disableDoubleClickZoom default false
23903  * @cfg {Boolean} scrollwheel default true
23904  * @cfg {Boolean} streetViewControl default false
23905  * @cfg {Number} radius default 0
23906  * @cfg {String} locationName
23907  * @cfg {Boolean} draggable default true
23908  * @cfg {Boolean} enableAutocomplete default false
23909  * @cfg {Boolean} enableReverseGeocode default true
23910  * @cfg {String} markerTitle
23911  * 
23912  * @constructor
23913  * Create a new LocationPicker
23914  * @param {Object} config The config object
23915  */
23916
23917
23918 Roo.bootstrap.LocationPicker = function(config){
23919     
23920     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23921     
23922     this.addEvents({
23923         /**
23924          * @event initial
23925          * Fires when the picker initialized.
23926          * @param {Roo.bootstrap.LocationPicker} this
23927          * @param {Google Location} location
23928          */
23929         initial : true,
23930         /**
23931          * @event positionchanged
23932          * Fires when the picker position changed.
23933          * @param {Roo.bootstrap.LocationPicker} this
23934          * @param {Google Location} location
23935          */
23936         positionchanged : true,
23937         /**
23938          * @event resize
23939          * Fires when the map resize.
23940          * @param {Roo.bootstrap.LocationPicker} this
23941          */
23942         resize : true,
23943         /**
23944          * @event show
23945          * Fires when the map show.
23946          * @param {Roo.bootstrap.LocationPicker} this
23947          */
23948         show : true,
23949         /**
23950          * @event hide
23951          * Fires when the map hide.
23952          * @param {Roo.bootstrap.LocationPicker} this
23953          */
23954         hide : true,
23955         /**
23956          * @event mapClick
23957          * Fires when click the map.
23958          * @param {Roo.bootstrap.LocationPicker} this
23959          * @param {Map event} e
23960          */
23961         mapClick : true,
23962         /**
23963          * @event mapRightClick
23964          * Fires when right click the map.
23965          * @param {Roo.bootstrap.LocationPicker} this
23966          * @param {Map event} e
23967          */
23968         mapRightClick : true,
23969         /**
23970          * @event markerClick
23971          * Fires when click the marker.
23972          * @param {Roo.bootstrap.LocationPicker} this
23973          * @param {Map event} e
23974          */
23975         markerClick : true,
23976         /**
23977          * @event markerRightClick
23978          * Fires when right click the marker.
23979          * @param {Roo.bootstrap.LocationPicker} this
23980          * @param {Map event} e
23981          */
23982         markerRightClick : true,
23983         /**
23984          * @event OverlayViewDraw
23985          * Fires when OverlayView Draw
23986          * @param {Roo.bootstrap.LocationPicker} this
23987          */
23988         OverlayViewDraw : true,
23989         /**
23990          * @event OverlayViewOnAdd
23991          * Fires when OverlayView Draw
23992          * @param {Roo.bootstrap.LocationPicker} this
23993          */
23994         OverlayViewOnAdd : true,
23995         /**
23996          * @event OverlayViewOnRemove
23997          * Fires when OverlayView Draw
23998          * @param {Roo.bootstrap.LocationPicker} this
23999          */
24000         OverlayViewOnRemove : true,
24001         /**
24002          * @event OverlayViewShow
24003          * Fires when OverlayView Draw
24004          * @param {Roo.bootstrap.LocationPicker} this
24005          * @param {Pixel} cpx
24006          */
24007         OverlayViewShow : true,
24008         /**
24009          * @event OverlayViewHide
24010          * Fires when OverlayView Draw
24011          * @param {Roo.bootstrap.LocationPicker} this
24012          */
24013         OverlayViewHide : true,
24014         /**
24015          * @event loadexception
24016          * Fires when load google lib failed.
24017          * @param {Roo.bootstrap.LocationPicker} this
24018          */
24019         loadexception : true
24020     });
24021         
24022 };
24023
24024 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24025     
24026     gMapContext: false,
24027     
24028     latitude: 0,
24029     longitude: 0,
24030     zoom: 15,
24031     mapTypeId: false,
24032     mapTypeControl: false,
24033     disableDoubleClickZoom: false,
24034     scrollwheel: true,
24035     streetViewControl: false,
24036     radius: 0,
24037     locationName: '',
24038     draggable: true,
24039     enableAutocomplete: false,
24040     enableReverseGeocode: true,
24041     markerTitle: '',
24042     
24043     getAutoCreate: function()
24044     {
24045
24046         var cfg = {
24047             tag: 'div',
24048             cls: 'roo-location-picker'
24049         };
24050         
24051         return cfg
24052     },
24053     
24054     initEvents: function(ct, position)
24055     {       
24056         if(!this.el.getWidth() || this.isApplied()){
24057             return;
24058         }
24059         
24060         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24061         
24062         this.initial();
24063     },
24064     
24065     initial: function()
24066     {
24067         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24068             this.fireEvent('loadexception', this);
24069             return;
24070         }
24071         
24072         if(!this.mapTypeId){
24073             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24074         }
24075         
24076         this.gMapContext = this.GMapContext();
24077         
24078         this.initOverlayView();
24079         
24080         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24081         
24082         var _this = this;
24083                 
24084         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24085             _this.setPosition(_this.gMapContext.marker.position);
24086         });
24087         
24088         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24089             _this.fireEvent('mapClick', this, event);
24090             
24091         });
24092
24093         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24094             _this.fireEvent('mapRightClick', this, event);
24095             
24096         });
24097         
24098         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24099             _this.fireEvent('markerClick', this, event);
24100             
24101         });
24102
24103         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24104             _this.fireEvent('markerRightClick', this, event);
24105             
24106         });
24107         
24108         this.setPosition(this.gMapContext.location);
24109         
24110         this.fireEvent('initial', this, this.gMapContext.location);
24111     },
24112     
24113     initOverlayView: function()
24114     {
24115         var _this = this;
24116         
24117         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24118             
24119             draw: function()
24120             {
24121                 _this.fireEvent('OverlayViewDraw', _this);
24122             },
24123             
24124             onAdd: function()
24125             {
24126                 _this.fireEvent('OverlayViewOnAdd', _this);
24127             },
24128             
24129             onRemove: function()
24130             {
24131                 _this.fireEvent('OverlayViewOnRemove', _this);
24132             },
24133             
24134             show: function(cpx)
24135             {
24136                 _this.fireEvent('OverlayViewShow', _this, cpx);
24137             },
24138             
24139             hide: function()
24140             {
24141                 _this.fireEvent('OverlayViewHide', _this);
24142             }
24143             
24144         });
24145     },
24146     
24147     fromLatLngToContainerPixel: function(event)
24148     {
24149         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24150     },
24151     
24152     isApplied: function() 
24153     {
24154         return this.getGmapContext() == false ? false : true;
24155     },
24156     
24157     getGmapContext: function() 
24158     {
24159         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24160     },
24161     
24162     GMapContext: function() 
24163     {
24164         var position = new google.maps.LatLng(this.latitude, this.longitude);
24165         
24166         var _map = new google.maps.Map(this.el.dom, {
24167             center: position,
24168             zoom: this.zoom,
24169             mapTypeId: this.mapTypeId,
24170             mapTypeControl: this.mapTypeControl,
24171             disableDoubleClickZoom: this.disableDoubleClickZoom,
24172             scrollwheel: this.scrollwheel,
24173             streetViewControl: this.streetViewControl,
24174             locationName: this.locationName,
24175             draggable: this.draggable,
24176             enableAutocomplete: this.enableAutocomplete,
24177             enableReverseGeocode: this.enableReverseGeocode
24178         });
24179         
24180         var _marker = new google.maps.Marker({
24181             position: position,
24182             map: _map,
24183             title: this.markerTitle,
24184             draggable: this.draggable
24185         });
24186         
24187         return {
24188             map: _map,
24189             marker: _marker,
24190             circle: null,
24191             location: position,
24192             radius: this.radius,
24193             locationName: this.locationName,
24194             addressComponents: {
24195                 formatted_address: null,
24196                 addressLine1: null,
24197                 addressLine2: null,
24198                 streetName: null,
24199                 streetNumber: null,
24200                 city: null,
24201                 district: null,
24202                 state: null,
24203                 stateOrProvince: null
24204             },
24205             settings: this,
24206             domContainer: this.el.dom,
24207             geodecoder: new google.maps.Geocoder()
24208         };
24209     },
24210     
24211     drawCircle: function(center, radius, options) 
24212     {
24213         if (this.gMapContext.circle != null) {
24214             this.gMapContext.circle.setMap(null);
24215         }
24216         if (radius > 0) {
24217             radius *= 1;
24218             options = Roo.apply({}, options, {
24219                 strokeColor: "#0000FF",
24220                 strokeOpacity: .35,
24221                 strokeWeight: 2,
24222                 fillColor: "#0000FF",
24223                 fillOpacity: .2
24224             });
24225             
24226             options.map = this.gMapContext.map;
24227             options.radius = radius;
24228             options.center = center;
24229             this.gMapContext.circle = new google.maps.Circle(options);
24230             return this.gMapContext.circle;
24231         }
24232         
24233         return null;
24234     },
24235     
24236     setPosition: function(location) 
24237     {
24238         this.gMapContext.location = location;
24239         this.gMapContext.marker.setPosition(location);
24240         this.gMapContext.map.panTo(location);
24241         this.drawCircle(location, this.gMapContext.radius, {});
24242         
24243         var _this = this;
24244         
24245         if (this.gMapContext.settings.enableReverseGeocode) {
24246             this.gMapContext.geodecoder.geocode({
24247                 latLng: this.gMapContext.location
24248             }, function(results, status) {
24249                 
24250                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24251                     _this.gMapContext.locationName = results[0].formatted_address;
24252                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24253                     
24254                     _this.fireEvent('positionchanged', this, location);
24255                 }
24256             });
24257             
24258             return;
24259         }
24260         
24261         this.fireEvent('positionchanged', this, location);
24262     },
24263     
24264     resize: function()
24265     {
24266         google.maps.event.trigger(this.gMapContext.map, "resize");
24267         
24268         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24269         
24270         this.fireEvent('resize', this);
24271     },
24272     
24273     setPositionByLatLng: function(latitude, longitude)
24274     {
24275         this.setPosition(new google.maps.LatLng(latitude, longitude));
24276     },
24277     
24278     getCurrentPosition: function() 
24279     {
24280         return {
24281             latitude: this.gMapContext.location.lat(),
24282             longitude: this.gMapContext.location.lng()
24283         };
24284     },
24285     
24286     getAddressName: function() 
24287     {
24288         return this.gMapContext.locationName;
24289     },
24290     
24291     getAddressComponents: function() 
24292     {
24293         return this.gMapContext.addressComponents;
24294     },
24295     
24296     address_component_from_google_geocode: function(address_components) 
24297     {
24298         var result = {};
24299         
24300         for (var i = 0; i < address_components.length; i++) {
24301             var component = address_components[i];
24302             if (component.types.indexOf("postal_code") >= 0) {
24303                 result.postalCode = component.short_name;
24304             } else if (component.types.indexOf("street_number") >= 0) {
24305                 result.streetNumber = component.short_name;
24306             } else if (component.types.indexOf("route") >= 0) {
24307                 result.streetName = component.short_name;
24308             } else if (component.types.indexOf("neighborhood") >= 0) {
24309                 result.city = component.short_name;
24310             } else if (component.types.indexOf("locality") >= 0) {
24311                 result.city = component.short_name;
24312             } else if (component.types.indexOf("sublocality") >= 0) {
24313                 result.district = component.short_name;
24314             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24315                 result.stateOrProvince = component.short_name;
24316             } else if (component.types.indexOf("country") >= 0) {
24317                 result.country = component.short_name;
24318             }
24319         }
24320         
24321         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24322         result.addressLine2 = "";
24323         return result;
24324     },
24325     
24326     setZoomLevel: function(zoom)
24327     {
24328         this.gMapContext.map.setZoom(zoom);
24329     },
24330     
24331     show: function()
24332     {
24333         if(!this.el){
24334             return;
24335         }
24336         
24337         this.el.show();
24338         
24339         this.resize();
24340         
24341         this.fireEvent('show', this);
24342     },
24343     
24344     hide: function()
24345     {
24346         if(!this.el){
24347             return;
24348         }
24349         
24350         this.el.hide();
24351         
24352         this.fireEvent('hide', this);
24353     }
24354     
24355 });
24356
24357 Roo.apply(Roo.bootstrap.LocationPicker, {
24358     
24359     OverlayView : function(map, options)
24360     {
24361         options = options || {};
24362         
24363         this.setMap(map);
24364     }
24365     
24366     
24367 });/*
24368  * - LGPL
24369  *
24370  * Alert
24371  * 
24372  */
24373
24374 /**
24375  * @class Roo.bootstrap.Alert
24376  * @extends Roo.bootstrap.Component
24377  * Bootstrap Alert class
24378  * @cfg {String} title The title of alert
24379  * @cfg {String} html The content of alert
24380  * @cfg {String} weight (  success | info | warning | danger )
24381  * @cfg {String} faicon font-awesomeicon
24382  * 
24383  * @constructor
24384  * Create a new alert
24385  * @param {Object} config The config object
24386  */
24387
24388
24389 Roo.bootstrap.Alert = function(config){
24390     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24391     
24392 };
24393
24394 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24395     
24396     title: '',
24397     html: '',
24398     weight: false,
24399     faicon: false,
24400     
24401     getAutoCreate : function()
24402     {
24403         
24404         var cfg = {
24405             tag : 'div',
24406             cls : 'alert',
24407             cn : [
24408                 {
24409                     tag : 'i',
24410                     cls : 'roo-alert-icon'
24411                     
24412                 },
24413                 {
24414                     tag : 'b',
24415                     cls : 'roo-alert-title',
24416                     html : this.title
24417                 },
24418                 {
24419                     tag : 'span',
24420                     cls : 'roo-alert-text',
24421                     html : this.html
24422                 }
24423             ]
24424         };
24425         
24426         if(this.faicon){
24427             cfg.cn[0].cls += ' fa ' + this.faicon;
24428         }
24429         
24430         if(this.weight){
24431             cfg.cls += ' alert-' + this.weight;
24432         }
24433         
24434         return cfg;
24435     },
24436     
24437     initEvents: function() 
24438     {
24439         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24440     },
24441     
24442     setTitle : function(str)
24443     {
24444         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24445     },
24446     
24447     setText : function(str)
24448     {
24449         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24450     },
24451     
24452     setWeight : function(weight)
24453     {
24454         if(this.weight){
24455             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24456         }
24457         
24458         this.weight = weight;
24459         
24460         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24461     },
24462     
24463     setIcon : function(icon)
24464     {
24465         if(this.faicon){
24466             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24467         }
24468         
24469         this.faicon = icon;
24470         
24471         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24472     },
24473     
24474     hide: function() 
24475     {
24476         this.el.hide();   
24477     },
24478     
24479     show: function() 
24480     {  
24481         this.el.show();   
24482     }
24483     
24484 });
24485
24486  
24487 /*
24488 * Licence: LGPL
24489 */
24490
24491 /**
24492  * @class Roo.bootstrap.UploadCropbox
24493  * @extends Roo.bootstrap.Component
24494  * Bootstrap UploadCropbox class
24495  * @cfg {String} emptyText show when image has been loaded
24496  * @cfg {String} rotateNotify show when image too small to rotate
24497  * @cfg {Number} errorTimeout default 3000
24498  * @cfg {Number} minWidth default 300
24499  * @cfg {Number} minHeight default 300
24500  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24501  * @cfg {Boolean} isDocument (true|false) default false
24502  * @cfg {String} url action url
24503  * @cfg {String} paramName default 'imageUpload'
24504  * @cfg {String} method default POST
24505  * @cfg {Boolean} loadMask (true|false) default true
24506  * @cfg {Boolean} loadingText default 'Loading...'
24507  * 
24508  * @constructor
24509  * Create a new UploadCropbox
24510  * @param {Object} config The config object
24511  */
24512
24513 Roo.bootstrap.UploadCropbox = function(config){
24514     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24515     
24516     this.addEvents({
24517         /**
24518          * @event beforeselectfile
24519          * Fire before select file
24520          * @param {Roo.bootstrap.UploadCropbox} this
24521          */
24522         "beforeselectfile" : true,
24523         /**
24524          * @event initial
24525          * Fire after initEvent
24526          * @param {Roo.bootstrap.UploadCropbox} this
24527          */
24528         "initial" : true,
24529         /**
24530          * @event crop
24531          * Fire after initEvent
24532          * @param {Roo.bootstrap.UploadCropbox} this
24533          * @param {String} data
24534          */
24535         "crop" : true,
24536         /**
24537          * @event prepare
24538          * Fire when preparing the file data
24539          * @param {Roo.bootstrap.UploadCropbox} this
24540          * @param {Object} file
24541          */
24542         "prepare" : true,
24543         /**
24544          * @event exception
24545          * Fire when get exception
24546          * @param {Roo.bootstrap.UploadCropbox} this
24547          * @param {XMLHttpRequest} xhr
24548          */
24549         "exception" : true,
24550         /**
24551          * @event beforeloadcanvas
24552          * Fire before load the canvas
24553          * @param {Roo.bootstrap.UploadCropbox} this
24554          * @param {String} src
24555          */
24556         "beforeloadcanvas" : true,
24557         /**
24558          * @event trash
24559          * Fire when trash image
24560          * @param {Roo.bootstrap.UploadCropbox} this
24561          */
24562         "trash" : true,
24563         /**
24564          * @event download
24565          * Fire when download the image
24566          * @param {Roo.bootstrap.UploadCropbox} this
24567          */
24568         "download" : true,
24569         /**
24570          * @event footerbuttonclick
24571          * Fire when footerbuttonclick
24572          * @param {Roo.bootstrap.UploadCropbox} this
24573          * @param {String} type
24574          */
24575         "footerbuttonclick" : true,
24576         /**
24577          * @event resize
24578          * Fire when resize
24579          * @param {Roo.bootstrap.UploadCropbox} this
24580          */
24581         "resize" : true,
24582         /**
24583          * @event rotate
24584          * Fire when rotate the image
24585          * @param {Roo.bootstrap.UploadCropbox} this
24586          * @param {String} pos
24587          */
24588         "rotate" : true,
24589         /**
24590          * @event inspect
24591          * Fire when inspect the file
24592          * @param {Roo.bootstrap.UploadCropbox} this
24593          * @param {Object} file
24594          */
24595         "inspect" : true,
24596         /**
24597          * @event upload
24598          * Fire when xhr upload the file
24599          * @param {Roo.bootstrap.UploadCropbox} this
24600          * @param {Object} data
24601          */
24602         "upload" : true,
24603         /**
24604          * @event arrange
24605          * Fire when arrange the file data
24606          * @param {Roo.bootstrap.UploadCropbox} this
24607          * @param {Object} formData
24608          */
24609         "arrange" : true
24610     });
24611     
24612     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24613 };
24614
24615 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24616     
24617     emptyText : 'Click to upload image',
24618     rotateNotify : 'Image is too small to rotate',
24619     errorTimeout : 3000,
24620     scale : 0,
24621     baseScale : 1,
24622     rotate : 0,
24623     dragable : false,
24624     pinching : false,
24625     mouseX : 0,
24626     mouseY : 0,
24627     cropData : false,
24628     minWidth : 300,
24629     minHeight : 300,
24630     file : false,
24631     exif : {},
24632     baseRotate : 1,
24633     cropType : 'image/jpeg',
24634     buttons : false,
24635     canvasLoaded : false,
24636     isDocument : false,
24637     method : 'POST',
24638     paramName : 'imageUpload',
24639     loadMask : true,
24640     loadingText : 'Loading...',
24641     maskEl : false,
24642     
24643     getAutoCreate : function()
24644     {
24645         var cfg = {
24646             tag : 'div',
24647             cls : 'roo-upload-cropbox',
24648             cn : [
24649                 {
24650                     tag : 'input',
24651                     cls : 'roo-upload-cropbox-selector',
24652                     type : 'file'
24653                 },
24654                 {
24655                     tag : 'div',
24656                     cls : 'roo-upload-cropbox-body',
24657                     style : 'cursor:pointer',
24658                     cn : [
24659                         {
24660                             tag : 'div',
24661                             cls : 'roo-upload-cropbox-preview'
24662                         },
24663                         {
24664                             tag : 'div',
24665                             cls : 'roo-upload-cropbox-thumb'
24666                         },
24667                         {
24668                             tag : 'div',
24669                             cls : 'roo-upload-cropbox-empty-notify',
24670                             html : this.emptyText
24671                         },
24672                         {
24673                             tag : 'div',
24674                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24675                             html : this.rotateNotify
24676                         }
24677                     ]
24678                 },
24679                 {
24680                     tag : 'div',
24681                     cls : 'roo-upload-cropbox-footer',
24682                     cn : {
24683                         tag : 'div',
24684                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24685                         cn : []
24686                     }
24687                 }
24688             ]
24689         };
24690         
24691         return cfg;
24692     },
24693     
24694     onRender : function(ct, position)
24695     {
24696         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24697         
24698         if (this.buttons.length) {
24699             
24700             Roo.each(this.buttons, function(bb) {
24701                 
24702                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24703                 
24704                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24705                 
24706             }, this);
24707         }
24708         
24709         if(this.loadMask){
24710             this.maskEl = this.el;
24711         }
24712     },
24713     
24714     initEvents : function()
24715     {
24716         this.urlAPI = (window.createObjectURL && window) || 
24717                                 (window.URL && URL.revokeObjectURL && URL) || 
24718                                 (window.webkitURL && webkitURL);
24719                         
24720         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24721         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24722         
24723         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24724         this.selectorEl.hide();
24725         
24726         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24727         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24728         
24729         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24730         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24731         this.thumbEl.hide();
24732         
24733         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24734         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24735         
24736         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24737         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24738         this.errorEl.hide();
24739         
24740         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24741         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24742         this.footerEl.hide();
24743         
24744         this.setThumbBoxSize();
24745         
24746         this.bind();
24747         
24748         this.resize();
24749         
24750         this.fireEvent('initial', this);
24751     },
24752
24753     bind : function()
24754     {
24755         var _this = this;
24756         
24757         window.addEventListener("resize", function() { _this.resize(); } );
24758         
24759         this.bodyEl.on('click', this.beforeSelectFile, this);
24760         
24761         if(Roo.isTouch){
24762             this.bodyEl.on('touchstart', this.onTouchStart, this);
24763             this.bodyEl.on('touchmove', this.onTouchMove, this);
24764             this.bodyEl.on('touchend', this.onTouchEnd, this);
24765         }
24766         
24767         if(!Roo.isTouch){
24768             this.bodyEl.on('mousedown', this.onMouseDown, this);
24769             this.bodyEl.on('mousemove', this.onMouseMove, this);
24770             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24771             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24772             Roo.get(document).on('mouseup', this.onMouseUp, this);
24773         }
24774         
24775         this.selectorEl.on('change', this.onFileSelected, this);
24776     },
24777     
24778     reset : function()
24779     {    
24780         this.scale = 0;
24781         this.baseScale = 1;
24782         this.rotate = 0;
24783         this.baseRotate = 1;
24784         this.dragable = false;
24785         this.pinching = false;
24786         this.mouseX = 0;
24787         this.mouseY = 0;
24788         this.cropData = false;
24789         this.notifyEl.dom.innerHTML = this.emptyText;
24790         
24791         this.selectorEl.dom.value = '';
24792         
24793     },
24794     
24795     resize : function()
24796     {
24797         if(this.fireEvent('resize', this) != false){
24798             this.setThumbBoxPosition();
24799             this.setCanvasPosition();
24800         }
24801     },
24802     
24803     onFooterButtonClick : function(e, el, o, type)
24804     {
24805         switch (type) {
24806             case 'rotate-left' :
24807                 this.onRotateLeft(e);
24808                 break;
24809             case 'rotate-right' :
24810                 this.onRotateRight(e);
24811                 break;
24812             case 'picture' :
24813                 this.beforeSelectFile(e);
24814                 break;
24815             case 'trash' :
24816                 this.trash(e);
24817                 break;
24818             case 'crop' :
24819                 this.crop(e);
24820                 break;
24821             case 'download' :
24822                 this.download(e);
24823                 break;
24824             default :
24825                 break;
24826         }
24827         
24828         this.fireEvent('footerbuttonclick', this, type);
24829     },
24830     
24831     beforeSelectFile : function(e)
24832     {
24833         e.preventDefault();
24834         
24835         if(this.fireEvent('beforeselectfile', this) != false){
24836             this.selectorEl.dom.click();
24837         }
24838     },
24839     
24840     onFileSelected : function(e)
24841     {
24842         e.preventDefault();
24843         
24844         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24845             return;
24846         }
24847         
24848         var file = this.selectorEl.dom.files[0];
24849         
24850         if(this.fireEvent('inspect', this, file) != false){
24851             this.prepare(file);
24852         }
24853         
24854     },
24855     
24856     trash : function(e)
24857     {
24858         this.fireEvent('trash', this);
24859     },
24860     
24861     download : function(e)
24862     {
24863         this.fireEvent('download', this);
24864     },
24865     
24866     loadCanvas : function(src)
24867     {   
24868         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24869             
24870             this.reset();
24871             
24872             this.imageEl = document.createElement('img');
24873             
24874             var _this = this;
24875             
24876             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24877             
24878             this.imageEl.src = src;
24879         }
24880     },
24881     
24882     onLoadCanvas : function()
24883     {   
24884         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24885         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24886         
24887         this.bodyEl.un('click', this.beforeSelectFile, this);
24888         
24889         this.notifyEl.hide();
24890         this.thumbEl.show();
24891         this.footerEl.show();
24892         
24893         this.baseRotateLevel();
24894         
24895         if(this.isDocument){
24896             this.setThumbBoxSize();
24897         }
24898         
24899         this.setThumbBoxPosition();
24900         
24901         this.baseScaleLevel();
24902         
24903         this.draw();
24904         
24905         this.resize();
24906         
24907         this.canvasLoaded = true;
24908         
24909         if(this.loadMask){
24910             this.maskEl.unmask();
24911         }
24912         
24913     },
24914     
24915     setCanvasPosition : function()
24916     {   
24917         if(!this.canvasEl){
24918             return;
24919         }
24920         
24921         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24922         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24923         
24924         this.previewEl.setLeft(pw);
24925         this.previewEl.setTop(ph);
24926         
24927     },
24928     
24929     onMouseDown : function(e)
24930     {   
24931         e.stopEvent();
24932         
24933         this.dragable = true;
24934         this.pinching = false;
24935         
24936         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24937             this.dragable = false;
24938             return;
24939         }
24940         
24941         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24942         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24943         
24944     },
24945     
24946     onMouseMove : function(e)
24947     {   
24948         e.stopEvent();
24949         
24950         if(!this.canvasLoaded){
24951             return;
24952         }
24953         
24954         if (!this.dragable){
24955             return;
24956         }
24957         
24958         var minX = Math.ceil(this.thumbEl.getLeft(true));
24959         var minY = Math.ceil(this.thumbEl.getTop(true));
24960         
24961         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24962         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24963         
24964         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24965         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24966         
24967         x = x - this.mouseX;
24968         y = y - this.mouseY;
24969         
24970         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24971         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24972         
24973         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24974         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24975         
24976         this.previewEl.setLeft(bgX);
24977         this.previewEl.setTop(bgY);
24978         
24979         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24980         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24981     },
24982     
24983     onMouseUp : function(e)
24984     {   
24985         e.stopEvent();
24986         
24987         this.dragable = false;
24988     },
24989     
24990     onMouseWheel : function(e)
24991     {   
24992         e.stopEvent();
24993         
24994         this.startScale = this.scale;
24995         
24996         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24997         
24998         if(!this.zoomable()){
24999             this.scale = this.startScale;
25000             return;
25001         }
25002         
25003         this.draw();
25004         
25005         return;
25006     },
25007     
25008     zoomable : function()
25009     {
25010         var minScale = this.thumbEl.getWidth() / this.minWidth;
25011         
25012         if(this.minWidth < this.minHeight){
25013             minScale = this.thumbEl.getHeight() / this.minHeight;
25014         }
25015         
25016         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25017         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25018         
25019         if(
25020                 this.isDocument &&
25021                 (this.rotate == 0 || this.rotate == 180) && 
25022                 (
25023                     width > this.imageEl.OriginWidth || 
25024                     height > this.imageEl.OriginHeight ||
25025                     (width < this.minWidth && height < this.minHeight)
25026                 )
25027         ){
25028             return false;
25029         }
25030         
25031         if(
25032                 this.isDocument &&
25033                 (this.rotate == 90 || this.rotate == 270) && 
25034                 (
25035                     width > this.imageEl.OriginWidth || 
25036                     height > this.imageEl.OriginHeight ||
25037                     (width < this.minHeight && height < this.minWidth)
25038                 )
25039         ){
25040             return false;
25041         }
25042         
25043         if(
25044                 !this.isDocument &&
25045                 (this.rotate == 0 || this.rotate == 180) && 
25046                 (
25047                     width < this.minWidth || 
25048                     width > this.imageEl.OriginWidth || 
25049                     height < this.minHeight || 
25050                     height > this.imageEl.OriginHeight
25051                 )
25052         ){
25053             return false;
25054         }
25055         
25056         if(
25057                 !this.isDocument &&
25058                 (this.rotate == 90 || this.rotate == 270) && 
25059                 (
25060                     width < this.minHeight || 
25061                     width > this.imageEl.OriginWidth || 
25062                     height < this.minWidth || 
25063                     height > this.imageEl.OriginHeight
25064                 )
25065         ){
25066             return false;
25067         }
25068         
25069         return true;
25070         
25071     },
25072     
25073     onRotateLeft : function(e)
25074     {   
25075         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25076             
25077             var minScale = this.thumbEl.getWidth() / this.minWidth;
25078             
25079             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25080             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25081             
25082             this.startScale = this.scale;
25083             
25084             while (this.getScaleLevel() < minScale){
25085             
25086                 this.scale = this.scale + 1;
25087                 
25088                 if(!this.zoomable()){
25089                     break;
25090                 }
25091                 
25092                 if(
25093                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25094                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25095                 ){
25096                     continue;
25097                 }
25098                 
25099                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25100
25101                 this.draw();
25102                 
25103                 return;
25104             }
25105             
25106             this.scale = this.startScale;
25107             
25108             this.onRotateFail();
25109             
25110             return false;
25111         }
25112         
25113         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25114
25115         if(this.isDocument){
25116             this.setThumbBoxSize();
25117             this.setThumbBoxPosition();
25118             this.setCanvasPosition();
25119         }
25120         
25121         this.draw();
25122         
25123         this.fireEvent('rotate', this, 'left');
25124         
25125     },
25126     
25127     onRotateRight : function(e)
25128     {
25129         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25130             
25131             var minScale = this.thumbEl.getWidth() / this.minWidth;
25132         
25133             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25134             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25135             
25136             this.startScale = this.scale;
25137             
25138             while (this.getScaleLevel() < minScale){
25139             
25140                 this.scale = this.scale + 1;
25141                 
25142                 if(!this.zoomable()){
25143                     break;
25144                 }
25145                 
25146                 if(
25147                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25148                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25149                 ){
25150                     continue;
25151                 }
25152                 
25153                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25154
25155                 this.draw();
25156                 
25157                 return;
25158             }
25159             
25160             this.scale = this.startScale;
25161             
25162             this.onRotateFail();
25163             
25164             return false;
25165         }
25166         
25167         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25168
25169         if(this.isDocument){
25170             this.setThumbBoxSize();
25171             this.setThumbBoxPosition();
25172             this.setCanvasPosition();
25173         }
25174         
25175         this.draw();
25176         
25177         this.fireEvent('rotate', this, 'right');
25178     },
25179     
25180     onRotateFail : function()
25181     {
25182         this.errorEl.show(true);
25183         
25184         var _this = this;
25185         
25186         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25187     },
25188     
25189     draw : function()
25190     {
25191         this.previewEl.dom.innerHTML = '';
25192         
25193         var canvasEl = document.createElement("canvas");
25194         
25195         var contextEl = canvasEl.getContext("2d");
25196         
25197         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25198         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25199         var center = this.imageEl.OriginWidth / 2;
25200         
25201         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25202             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25203             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25204             center = this.imageEl.OriginHeight / 2;
25205         }
25206         
25207         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25208         
25209         contextEl.translate(center, center);
25210         contextEl.rotate(this.rotate * Math.PI / 180);
25211
25212         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25213         
25214         this.canvasEl = document.createElement("canvas");
25215         
25216         this.contextEl = this.canvasEl.getContext("2d");
25217         
25218         switch (this.rotate) {
25219             case 0 :
25220                 
25221                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25222                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25223                 
25224                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25225                 
25226                 break;
25227             case 90 : 
25228                 
25229                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25230                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25231                 
25232                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25233                     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);
25234                     break;
25235                 }
25236                 
25237                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25238                 
25239                 break;
25240             case 180 :
25241                 
25242                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25243                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25244                 
25245                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25246                     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);
25247                     break;
25248                 }
25249                 
25250                 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);
25251                 
25252                 break;
25253             case 270 :
25254                 
25255                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25256                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25257         
25258                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25259                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25260                     break;
25261                 }
25262                 
25263                 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);
25264                 
25265                 break;
25266             default : 
25267                 break;
25268         }
25269         
25270         this.previewEl.appendChild(this.canvasEl);
25271         
25272         this.setCanvasPosition();
25273     },
25274     
25275     crop : function()
25276     {
25277         if(!this.canvasLoaded){
25278             return;
25279         }
25280         
25281         var imageCanvas = document.createElement("canvas");
25282         
25283         var imageContext = imageCanvas.getContext("2d");
25284         
25285         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25286         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25287         
25288         var center = imageCanvas.width / 2;
25289         
25290         imageContext.translate(center, center);
25291         
25292         imageContext.rotate(this.rotate * Math.PI / 180);
25293         
25294         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25295         
25296         var canvas = document.createElement("canvas");
25297         
25298         var context = canvas.getContext("2d");
25299                 
25300         canvas.width = this.minWidth;
25301         canvas.height = this.minHeight;
25302
25303         switch (this.rotate) {
25304             case 0 :
25305                 
25306                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25307                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25308                 
25309                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25310                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25311                 
25312                 var targetWidth = this.minWidth - 2 * x;
25313                 var targetHeight = this.minHeight - 2 * y;
25314                 
25315                 var scale = 1;
25316                 
25317                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25318                     scale = targetWidth / width;
25319                 }
25320                 
25321                 if(x > 0 && y == 0){
25322                     scale = targetHeight / height;
25323                 }
25324                 
25325                 if(x > 0 && y > 0){
25326                     scale = targetWidth / width;
25327                     
25328                     if(width < height){
25329                         scale = targetHeight / height;
25330                     }
25331                 }
25332                 
25333                 context.scale(scale, scale);
25334                 
25335                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25336                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25337
25338                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25339                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25340
25341                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25342                 
25343                 break;
25344             case 90 : 
25345                 
25346                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25347                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25348                 
25349                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25350                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25351                 
25352                 var targetWidth = this.minWidth - 2 * x;
25353                 var targetHeight = this.minHeight - 2 * y;
25354                 
25355                 var scale = 1;
25356                 
25357                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25358                     scale = targetWidth / width;
25359                 }
25360                 
25361                 if(x > 0 && y == 0){
25362                     scale = targetHeight / height;
25363                 }
25364                 
25365                 if(x > 0 && y > 0){
25366                     scale = targetWidth / width;
25367                     
25368                     if(width < height){
25369                         scale = targetHeight / height;
25370                     }
25371                 }
25372                 
25373                 context.scale(scale, scale);
25374                 
25375                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25376                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25377
25378                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25379                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25380                 
25381                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25382                 
25383                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25384                 
25385                 break;
25386             case 180 :
25387                 
25388                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25389                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25390                 
25391                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25392                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25393                 
25394                 var targetWidth = this.minWidth - 2 * x;
25395                 var targetHeight = this.minHeight - 2 * y;
25396                 
25397                 var scale = 1;
25398                 
25399                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25400                     scale = targetWidth / width;
25401                 }
25402                 
25403                 if(x > 0 && y == 0){
25404                     scale = targetHeight / height;
25405                 }
25406                 
25407                 if(x > 0 && y > 0){
25408                     scale = targetWidth / width;
25409                     
25410                     if(width < height){
25411                         scale = targetHeight / height;
25412                     }
25413                 }
25414                 
25415                 context.scale(scale, scale);
25416                 
25417                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25418                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25419
25420                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25421                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25422
25423                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25424                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25425                 
25426                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25427                 
25428                 break;
25429             case 270 :
25430                 
25431                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25432                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25433                 
25434                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25435                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25436                 
25437                 var targetWidth = this.minWidth - 2 * x;
25438                 var targetHeight = this.minHeight - 2 * y;
25439                 
25440                 var scale = 1;
25441                 
25442                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25443                     scale = targetWidth / width;
25444                 }
25445                 
25446                 if(x > 0 && y == 0){
25447                     scale = targetHeight / height;
25448                 }
25449                 
25450                 if(x > 0 && y > 0){
25451                     scale = targetWidth / width;
25452                     
25453                     if(width < height){
25454                         scale = targetHeight / height;
25455                     }
25456                 }
25457                 
25458                 context.scale(scale, scale);
25459                 
25460                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25461                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25462
25463                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25464                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25465                 
25466                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25467                 
25468                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25469                 
25470                 break;
25471             default : 
25472                 break;
25473         }
25474         
25475         this.cropData = canvas.toDataURL(this.cropType);
25476         
25477         if(this.fireEvent('crop', this, this.cropData) !== false){
25478             this.process(this.file, this.cropData);
25479         }
25480         
25481         return;
25482         
25483     },
25484     
25485     setThumbBoxSize : function()
25486     {
25487         var width, height;
25488         
25489         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25490             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25491             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25492             
25493             this.minWidth = width;
25494             this.minHeight = height;
25495             
25496             if(this.rotate == 90 || this.rotate == 270){
25497                 this.minWidth = height;
25498                 this.minHeight = width;
25499             }
25500         }
25501         
25502         height = 300;
25503         width = Math.ceil(this.minWidth * height / this.minHeight);
25504         
25505         if(this.minWidth > this.minHeight){
25506             width = 300;
25507             height = Math.ceil(this.minHeight * width / this.minWidth);
25508         }
25509         
25510         this.thumbEl.setStyle({
25511             width : width + 'px',
25512             height : height + 'px'
25513         });
25514
25515         return;
25516             
25517     },
25518     
25519     setThumbBoxPosition : function()
25520     {
25521         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25522         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25523         
25524         this.thumbEl.setLeft(x);
25525         this.thumbEl.setTop(y);
25526         
25527     },
25528     
25529     baseRotateLevel : function()
25530     {
25531         this.baseRotate = 1;
25532         
25533         if(
25534                 typeof(this.exif) != 'undefined' &&
25535                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25536                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25537         ){
25538             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25539         }
25540         
25541         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25542         
25543     },
25544     
25545     baseScaleLevel : function()
25546     {
25547         var width, height;
25548         
25549         if(this.isDocument){
25550             
25551             if(this.baseRotate == 6 || this.baseRotate == 8){
25552             
25553                 height = this.thumbEl.getHeight();
25554                 this.baseScale = height / this.imageEl.OriginWidth;
25555
25556                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25557                     width = this.thumbEl.getWidth();
25558                     this.baseScale = width / this.imageEl.OriginHeight;
25559                 }
25560
25561                 return;
25562             }
25563
25564             height = this.thumbEl.getHeight();
25565             this.baseScale = height / this.imageEl.OriginHeight;
25566
25567             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25568                 width = this.thumbEl.getWidth();
25569                 this.baseScale = width / this.imageEl.OriginWidth;
25570             }
25571
25572             return;
25573         }
25574         
25575         if(this.baseRotate == 6 || this.baseRotate == 8){
25576             
25577             width = this.thumbEl.getHeight();
25578             this.baseScale = width / this.imageEl.OriginHeight;
25579             
25580             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25581                 height = this.thumbEl.getWidth();
25582                 this.baseScale = height / this.imageEl.OriginHeight;
25583             }
25584             
25585             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25586                 height = this.thumbEl.getWidth();
25587                 this.baseScale = height / this.imageEl.OriginHeight;
25588                 
25589                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25590                     width = this.thumbEl.getHeight();
25591                     this.baseScale = width / this.imageEl.OriginWidth;
25592                 }
25593             }
25594             
25595             return;
25596         }
25597         
25598         width = this.thumbEl.getWidth();
25599         this.baseScale = width / this.imageEl.OriginWidth;
25600         
25601         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25602             height = this.thumbEl.getHeight();
25603             this.baseScale = height / this.imageEl.OriginHeight;
25604         }
25605         
25606         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25607             
25608             height = this.thumbEl.getHeight();
25609             this.baseScale = height / this.imageEl.OriginHeight;
25610             
25611             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25612                 width = this.thumbEl.getWidth();
25613                 this.baseScale = width / this.imageEl.OriginWidth;
25614             }
25615             
25616         }
25617         
25618         return;
25619     },
25620     
25621     getScaleLevel : function()
25622     {
25623         return this.baseScale * Math.pow(1.1, this.scale);
25624     },
25625     
25626     onTouchStart : function(e)
25627     {
25628         if(!this.canvasLoaded){
25629             this.beforeSelectFile(e);
25630             return;
25631         }
25632         
25633         var touches = e.browserEvent.touches;
25634         
25635         if(!touches){
25636             return;
25637         }
25638         
25639         if(touches.length == 1){
25640             this.onMouseDown(e);
25641             return;
25642         }
25643         
25644         if(touches.length != 2){
25645             return;
25646         }
25647         
25648         var coords = [];
25649         
25650         for(var i = 0, finger; finger = touches[i]; i++){
25651             coords.push(finger.pageX, finger.pageY);
25652         }
25653         
25654         var x = Math.pow(coords[0] - coords[2], 2);
25655         var y = Math.pow(coords[1] - coords[3], 2);
25656         
25657         this.startDistance = Math.sqrt(x + y);
25658         
25659         this.startScale = this.scale;
25660         
25661         this.pinching = true;
25662         this.dragable = false;
25663         
25664     },
25665     
25666     onTouchMove : function(e)
25667     {
25668         if(!this.pinching && !this.dragable){
25669             return;
25670         }
25671         
25672         var touches = e.browserEvent.touches;
25673         
25674         if(!touches){
25675             return;
25676         }
25677         
25678         if(this.dragable){
25679             this.onMouseMove(e);
25680             return;
25681         }
25682         
25683         var coords = [];
25684         
25685         for(var i = 0, finger; finger = touches[i]; i++){
25686             coords.push(finger.pageX, finger.pageY);
25687         }
25688         
25689         var x = Math.pow(coords[0] - coords[2], 2);
25690         var y = Math.pow(coords[1] - coords[3], 2);
25691         
25692         this.endDistance = Math.sqrt(x + y);
25693         
25694         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25695         
25696         if(!this.zoomable()){
25697             this.scale = this.startScale;
25698             return;
25699         }
25700         
25701         this.draw();
25702         
25703     },
25704     
25705     onTouchEnd : function(e)
25706     {
25707         this.pinching = false;
25708         this.dragable = false;
25709         
25710     },
25711     
25712     process : function(file, crop)
25713     {
25714         if(this.loadMask){
25715             this.maskEl.mask(this.loadingText);
25716         }
25717         
25718         this.xhr = new XMLHttpRequest();
25719         
25720         file.xhr = this.xhr;
25721
25722         this.xhr.open(this.method, this.url, true);
25723         
25724         var headers = {
25725             "Accept": "application/json",
25726             "Cache-Control": "no-cache",
25727             "X-Requested-With": "XMLHttpRequest"
25728         };
25729         
25730         for (var headerName in headers) {
25731             var headerValue = headers[headerName];
25732             if (headerValue) {
25733                 this.xhr.setRequestHeader(headerName, headerValue);
25734             }
25735         }
25736         
25737         var _this = this;
25738         
25739         this.xhr.onload = function()
25740         {
25741             _this.xhrOnLoad(_this.xhr);
25742         }
25743         
25744         this.xhr.onerror = function()
25745         {
25746             _this.xhrOnError(_this.xhr);
25747         }
25748         
25749         var formData = new FormData();
25750
25751         formData.append('returnHTML', 'NO');
25752         
25753         if(crop){
25754             formData.append('crop', crop);
25755         }
25756         
25757         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25758             formData.append(this.paramName, file, file.name);
25759         }
25760         
25761         if(typeof(file.filename) != 'undefined'){
25762             formData.append('filename', file.filename);
25763         }
25764         
25765         if(typeof(file.mimetype) != 'undefined'){
25766             formData.append('mimetype', file.mimetype);
25767         }
25768         
25769         if(this.fireEvent('arrange', this, formData) != false){
25770             this.xhr.send(formData);
25771         };
25772     },
25773     
25774     xhrOnLoad : function(xhr)
25775     {
25776         if(this.loadMask){
25777             this.maskEl.unmask();
25778         }
25779         
25780         if (xhr.readyState !== 4) {
25781             this.fireEvent('exception', this, xhr);
25782             return;
25783         }
25784
25785         var response = Roo.decode(xhr.responseText);
25786         
25787         if(!response.success){
25788             this.fireEvent('exception', this, xhr);
25789             return;
25790         }
25791         
25792         var response = Roo.decode(xhr.responseText);
25793         
25794         this.fireEvent('upload', this, response);
25795         
25796     },
25797     
25798     xhrOnError : function()
25799     {
25800         if(this.loadMask){
25801             this.maskEl.unmask();
25802         }
25803         
25804         Roo.log('xhr on error');
25805         
25806         var response = Roo.decode(xhr.responseText);
25807           
25808         Roo.log(response);
25809         
25810     },
25811     
25812     prepare : function(file)
25813     {   
25814         if(this.loadMask){
25815             this.maskEl.mask(this.loadingText);
25816         }
25817         
25818         this.file = false;
25819         this.exif = {};
25820         
25821         if(typeof(file) === 'string'){
25822             this.loadCanvas(file);
25823             return;
25824         }
25825         
25826         if(!file || !this.urlAPI){
25827             return;
25828         }
25829         
25830         this.file = file;
25831         this.cropType = file.type;
25832         
25833         var _this = this;
25834         
25835         if(this.fireEvent('prepare', this, this.file) != false){
25836             
25837             var reader = new FileReader();
25838             
25839             reader.onload = function (e) {
25840                 if (e.target.error) {
25841                     Roo.log(e.target.error);
25842                     return;
25843                 }
25844                 
25845                 var buffer = e.target.result,
25846                     dataView = new DataView(buffer),
25847                     offset = 2,
25848                     maxOffset = dataView.byteLength - 4,
25849                     markerBytes,
25850                     markerLength;
25851                 
25852                 if (dataView.getUint16(0) === 0xffd8) {
25853                     while (offset < maxOffset) {
25854                         markerBytes = dataView.getUint16(offset);
25855                         
25856                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25857                             markerLength = dataView.getUint16(offset + 2) + 2;
25858                             if (offset + markerLength > dataView.byteLength) {
25859                                 Roo.log('Invalid meta data: Invalid segment size.');
25860                                 break;
25861                             }
25862                             
25863                             if(markerBytes == 0xffe1){
25864                                 _this.parseExifData(
25865                                     dataView,
25866                                     offset,
25867                                     markerLength
25868                                 );
25869                             }
25870                             
25871                             offset += markerLength;
25872                             
25873                             continue;
25874                         }
25875                         
25876                         break;
25877                     }
25878                     
25879                 }
25880                 
25881                 var url = _this.urlAPI.createObjectURL(_this.file);
25882                 
25883                 _this.loadCanvas(url);
25884                 
25885                 return;
25886             }
25887             
25888             reader.readAsArrayBuffer(this.file);
25889             
25890         }
25891         
25892     },
25893     
25894     parseExifData : function(dataView, offset, length)
25895     {
25896         var tiffOffset = offset + 10,
25897             littleEndian,
25898             dirOffset;
25899     
25900         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25901             // No Exif data, might be XMP data instead
25902             return;
25903         }
25904         
25905         // Check for the ASCII code for "Exif" (0x45786966):
25906         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25907             // No Exif data, might be XMP data instead
25908             return;
25909         }
25910         if (tiffOffset + 8 > dataView.byteLength) {
25911             Roo.log('Invalid Exif data: Invalid segment size.');
25912             return;
25913         }
25914         // Check for the two null bytes:
25915         if (dataView.getUint16(offset + 8) !== 0x0000) {
25916             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25917             return;
25918         }
25919         // Check the byte alignment:
25920         switch (dataView.getUint16(tiffOffset)) {
25921         case 0x4949:
25922             littleEndian = true;
25923             break;
25924         case 0x4D4D:
25925             littleEndian = false;
25926             break;
25927         default:
25928             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25929             return;
25930         }
25931         // Check for the TIFF tag marker (0x002A):
25932         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25933             Roo.log('Invalid Exif data: Missing TIFF marker.');
25934             return;
25935         }
25936         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25937         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25938         
25939         this.parseExifTags(
25940             dataView,
25941             tiffOffset,
25942             tiffOffset + dirOffset,
25943             littleEndian
25944         );
25945     },
25946     
25947     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25948     {
25949         var tagsNumber,
25950             dirEndOffset,
25951             i;
25952         if (dirOffset + 6 > dataView.byteLength) {
25953             Roo.log('Invalid Exif data: Invalid directory offset.');
25954             return;
25955         }
25956         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25957         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25958         if (dirEndOffset + 4 > dataView.byteLength) {
25959             Roo.log('Invalid Exif data: Invalid directory size.');
25960             return;
25961         }
25962         for (i = 0; i < tagsNumber; i += 1) {
25963             this.parseExifTag(
25964                 dataView,
25965                 tiffOffset,
25966                 dirOffset + 2 + 12 * i, // tag offset
25967                 littleEndian
25968             );
25969         }
25970         // Return the offset to the next directory:
25971         return dataView.getUint32(dirEndOffset, littleEndian);
25972     },
25973     
25974     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25975     {
25976         var tag = dataView.getUint16(offset, littleEndian);
25977         
25978         this.exif[tag] = this.getExifValue(
25979             dataView,
25980             tiffOffset,
25981             offset,
25982             dataView.getUint16(offset + 2, littleEndian), // tag type
25983             dataView.getUint32(offset + 4, littleEndian), // tag length
25984             littleEndian
25985         );
25986     },
25987     
25988     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25989     {
25990         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25991             tagSize,
25992             dataOffset,
25993             values,
25994             i,
25995             str,
25996             c;
25997     
25998         if (!tagType) {
25999             Roo.log('Invalid Exif data: Invalid tag type.');
26000             return;
26001         }
26002         
26003         tagSize = tagType.size * length;
26004         // Determine if the value is contained in the dataOffset bytes,
26005         // or if the value at the dataOffset is a pointer to the actual data:
26006         dataOffset = tagSize > 4 ?
26007                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26008         if (dataOffset + tagSize > dataView.byteLength) {
26009             Roo.log('Invalid Exif data: Invalid data offset.');
26010             return;
26011         }
26012         if (length === 1) {
26013             return tagType.getValue(dataView, dataOffset, littleEndian);
26014         }
26015         values = [];
26016         for (i = 0; i < length; i += 1) {
26017             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26018         }
26019         
26020         if (tagType.ascii) {
26021             str = '';
26022             // Concatenate the chars:
26023             for (i = 0; i < values.length; i += 1) {
26024                 c = values[i];
26025                 // Ignore the terminating NULL byte(s):
26026                 if (c === '\u0000') {
26027                     break;
26028                 }
26029                 str += c;
26030             }
26031             return str;
26032         }
26033         return values;
26034     }
26035     
26036 });
26037
26038 Roo.apply(Roo.bootstrap.UploadCropbox, {
26039     tags : {
26040         'Orientation': 0x0112
26041     },
26042     
26043     Orientation: {
26044             1: 0, //'top-left',
26045 //            2: 'top-right',
26046             3: 180, //'bottom-right',
26047 //            4: 'bottom-left',
26048 //            5: 'left-top',
26049             6: 90, //'right-top',
26050 //            7: 'right-bottom',
26051             8: 270 //'left-bottom'
26052     },
26053     
26054     exifTagTypes : {
26055         // byte, 8-bit unsigned int:
26056         1: {
26057             getValue: function (dataView, dataOffset) {
26058                 return dataView.getUint8(dataOffset);
26059             },
26060             size: 1
26061         },
26062         // ascii, 8-bit byte:
26063         2: {
26064             getValue: function (dataView, dataOffset) {
26065                 return String.fromCharCode(dataView.getUint8(dataOffset));
26066             },
26067             size: 1,
26068             ascii: true
26069         },
26070         // short, 16 bit int:
26071         3: {
26072             getValue: function (dataView, dataOffset, littleEndian) {
26073                 return dataView.getUint16(dataOffset, littleEndian);
26074             },
26075             size: 2
26076         },
26077         // long, 32 bit int:
26078         4: {
26079             getValue: function (dataView, dataOffset, littleEndian) {
26080                 return dataView.getUint32(dataOffset, littleEndian);
26081             },
26082             size: 4
26083         },
26084         // rational = two long values, first is numerator, second is denominator:
26085         5: {
26086             getValue: function (dataView, dataOffset, littleEndian) {
26087                 return dataView.getUint32(dataOffset, littleEndian) /
26088                     dataView.getUint32(dataOffset + 4, littleEndian);
26089             },
26090             size: 8
26091         },
26092         // slong, 32 bit signed int:
26093         9: {
26094             getValue: function (dataView, dataOffset, littleEndian) {
26095                 return dataView.getInt32(dataOffset, littleEndian);
26096             },
26097             size: 4
26098         },
26099         // srational, two slongs, first is numerator, second is denominator:
26100         10: {
26101             getValue: function (dataView, dataOffset, littleEndian) {
26102                 return dataView.getInt32(dataOffset, littleEndian) /
26103                     dataView.getInt32(dataOffset + 4, littleEndian);
26104             },
26105             size: 8
26106         }
26107     },
26108     
26109     footer : {
26110         STANDARD : [
26111             {
26112                 tag : 'div',
26113                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26114                 action : 'rotate-left',
26115                 cn : [
26116                     {
26117                         tag : 'button',
26118                         cls : 'btn btn-default',
26119                         html : '<i class="fa fa-undo"></i>'
26120                     }
26121                 ]
26122             },
26123             {
26124                 tag : 'div',
26125                 cls : 'btn-group roo-upload-cropbox-picture',
26126                 action : 'picture',
26127                 cn : [
26128                     {
26129                         tag : 'button',
26130                         cls : 'btn btn-default',
26131                         html : '<i class="fa fa-picture-o"></i>'
26132                     }
26133                 ]
26134             },
26135             {
26136                 tag : 'div',
26137                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26138                 action : 'rotate-right',
26139                 cn : [
26140                     {
26141                         tag : 'button',
26142                         cls : 'btn btn-default',
26143                         html : '<i class="fa fa-repeat"></i>'
26144                     }
26145                 ]
26146             }
26147         ],
26148         DOCUMENT : [
26149             {
26150                 tag : 'div',
26151                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26152                 action : 'rotate-left',
26153                 cn : [
26154                     {
26155                         tag : 'button',
26156                         cls : 'btn btn-default',
26157                         html : '<i class="fa fa-undo"></i>'
26158                     }
26159                 ]
26160             },
26161             {
26162                 tag : 'div',
26163                 cls : 'btn-group roo-upload-cropbox-download',
26164                 action : 'download',
26165                 cn : [
26166                     {
26167                         tag : 'button',
26168                         cls : 'btn btn-default',
26169                         html : '<i class="fa fa-download"></i>'
26170                     }
26171                 ]
26172             },
26173             {
26174                 tag : 'div',
26175                 cls : 'btn-group roo-upload-cropbox-crop',
26176                 action : 'crop',
26177                 cn : [
26178                     {
26179                         tag : 'button',
26180                         cls : 'btn btn-default',
26181                         html : '<i class="fa fa-crop"></i>'
26182                     }
26183                 ]
26184             },
26185             {
26186                 tag : 'div',
26187                 cls : 'btn-group roo-upload-cropbox-trash',
26188                 action : 'trash',
26189                 cn : [
26190                     {
26191                         tag : 'button',
26192                         cls : 'btn btn-default',
26193                         html : '<i class="fa fa-trash"></i>'
26194                     }
26195                 ]
26196             },
26197             {
26198                 tag : 'div',
26199                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26200                 action : 'rotate-right',
26201                 cn : [
26202                     {
26203                         tag : 'button',
26204                         cls : 'btn btn-default',
26205                         html : '<i class="fa fa-repeat"></i>'
26206                     }
26207                 ]
26208             }
26209         ],
26210         ROTATOR : [
26211             {
26212                 tag : 'div',
26213                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26214                 action : 'rotate-left',
26215                 cn : [
26216                     {
26217                         tag : 'button',
26218                         cls : 'btn btn-default',
26219                         html : '<i class="fa fa-undo"></i>'
26220                     }
26221                 ]
26222             },
26223             {
26224                 tag : 'div',
26225                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26226                 action : 'rotate-right',
26227                 cn : [
26228                     {
26229                         tag : 'button',
26230                         cls : 'btn btn-default',
26231                         html : '<i class="fa fa-repeat"></i>'
26232                     }
26233                 ]
26234             }
26235         ]
26236     }
26237 });
26238
26239 /*
26240 * Licence: LGPL
26241 */
26242
26243 /**
26244  * @class Roo.bootstrap.DocumentManager
26245  * @extends Roo.bootstrap.Component
26246  * Bootstrap DocumentManager class
26247  * @cfg {String} paramName default 'imageUpload'
26248  * @cfg {String} method default POST
26249  * @cfg {String} url action url
26250  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26251  * @cfg {Boolean} multiple multiple upload default true
26252  * @cfg {Number} thumbSize default 300
26253  * @cfg {String} fieldLabel
26254  * @cfg {Number} labelWidth default 4
26255  * @cfg {String} labelAlign (left|top) default left
26256  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26257  * 
26258  * @constructor
26259  * Create a new DocumentManager
26260  * @param {Object} config The config object
26261  */
26262
26263 Roo.bootstrap.DocumentManager = function(config){
26264     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26265     
26266     this.addEvents({
26267         /**
26268          * @event initial
26269          * Fire when initial the DocumentManager
26270          * @param {Roo.bootstrap.DocumentManager} this
26271          */
26272         "initial" : true,
26273         /**
26274          * @event inspect
26275          * inspect selected file
26276          * @param {Roo.bootstrap.DocumentManager} this
26277          * @param {File} file
26278          */
26279         "inspect" : true,
26280         /**
26281          * @event exception
26282          * Fire when xhr load exception
26283          * @param {Roo.bootstrap.DocumentManager} this
26284          * @param {XMLHttpRequest} xhr
26285          */
26286         "exception" : true,
26287         /**
26288          * @event prepare
26289          * prepare the form data
26290          * @param {Roo.bootstrap.DocumentManager} this
26291          * @param {Object} formData
26292          */
26293         "prepare" : true,
26294         /**
26295          * @event remove
26296          * Fire when remove the file
26297          * @param {Roo.bootstrap.DocumentManager} this
26298          * @param {Object} file
26299          */
26300         "remove" : true,
26301         /**
26302          * @event refresh
26303          * Fire after refresh the file
26304          * @param {Roo.bootstrap.DocumentManager} this
26305          */
26306         "refresh" : true,
26307         /**
26308          * @event click
26309          * Fire after click the image
26310          * @param {Roo.bootstrap.DocumentManager} this
26311          * @param {Object} file
26312          */
26313         "click" : true,
26314         /**
26315          * @event edit
26316          * Fire when upload a image and editable set to true
26317          * @param {Roo.bootstrap.DocumentManager} this
26318          * @param {Object} file
26319          */
26320         "edit" : true,
26321         /**
26322          * @event beforeselectfile
26323          * Fire before select file
26324          * @param {Roo.bootstrap.DocumentManager} this
26325          */
26326         "beforeselectfile" : true,
26327         /**
26328          * @event process
26329          * Fire before process file
26330          * @param {Roo.bootstrap.DocumentManager} this
26331          * @param {Object} file
26332          */
26333         "process" : true
26334         
26335     });
26336 };
26337
26338 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26339     
26340     boxes : 0,
26341     inputName : '',
26342     thumbSize : 300,
26343     multiple : true,
26344     files : [],
26345     method : 'POST',
26346     url : '',
26347     paramName : 'imageUpload',
26348     fieldLabel : '',
26349     labelWidth : 4,
26350     labelAlign : 'left',
26351     editable : true,
26352     delegates : [],
26353     
26354     
26355     xhr : false, 
26356     
26357     getAutoCreate : function()
26358     {   
26359         var managerWidget = {
26360             tag : 'div',
26361             cls : 'roo-document-manager',
26362             cn : [
26363                 {
26364                     tag : 'input',
26365                     cls : 'roo-document-manager-selector',
26366                     type : 'file'
26367                 },
26368                 {
26369                     tag : 'div',
26370                     cls : 'roo-document-manager-uploader',
26371                     cn : [
26372                         {
26373                             tag : 'div',
26374                             cls : 'roo-document-manager-upload-btn',
26375                             html : '<i class="fa fa-plus"></i>'
26376                         }
26377                     ]
26378                     
26379                 }
26380             ]
26381         };
26382         
26383         var content = [
26384             {
26385                 tag : 'div',
26386                 cls : 'column col-md-12',
26387                 cn : managerWidget
26388             }
26389         ];
26390         
26391         if(this.fieldLabel.length){
26392             
26393             content = [
26394                 {
26395                     tag : 'div',
26396                     cls : 'column col-md-12',
26397                     html : this.fieldLabel
26398                 },
26399                 {
26400                     tag : 'div',
26401                     cls : 'column col-md-12',
26402                     cn : managerWidget
26403                 }
26404             ];
26405
26406             if(this.labelAlign == 'left'){
26407                 content = [
26408                     {
26409                         tag : 'div',
26410                         cls : 'column col-md-' + this.labelWidth,
26411                         html : this.fieldLabel
26412                     },
26413                     {
26414                         tag : 'div',
26415                         cls : 'column col-md-' + (12 - this.labelWidth),
26416                         cn : managerWidget
26417                     }
26418                 ];
26419                 
26420             }
26421         }
26422         
26423         var cfg = {
26424             tag : 'div',
26425             cls : 'row clearfix',
26426             cn : content
26427         };
26428         
26429         return cfg;
26430         
26431     },
26432     
26433     initEvents : function()
26434     {
26435         this.managerEl = this.el.select('.roo-document-manager', true).first();
26436         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26437         
26438         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26439         this.selectorEl.hide();
26440         
26441         if(this.multiple){
26442             this.selectorEl.attr('multiple', 'multiple');
26443         }
26444         
26445         this.selectorEl.on('change', this.onFileSelected, this);
26446         
26447         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26448         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26449         
26450         this.uploader.on('click', this.onUploaderClick, this);
26451         
26452         this.renderProgressDialog();
26453         
26454         var _this = this;
26455         
26456         window.addEventListener("resize", function() { _this.refresh(); } );
26457         
26458         this.fireEvent('initial', this);
26459     },
26460     
26461     renderProgressDialog : function()
26462     {
26463         var _this = this;
26464         
26465         this.progressDialog = new Roo.bootstrap.Modal({
26466             cls : 'roo-document-manager-progress-dialog',
26467             allow_close : false,
26468             title : '',
26469             buttons : [
26470                 {
26471                     name  :'cancel',
26472                     weight : 'danger',
26473                     html : 'Cancel'
26474                 }
26475             ], 
26476             listeners : { 
26477                 btnclick : function() {
26478                     _this.uploadCancel();
26479                     this.hide();
26480                 }
26481             }
26482         });
26483          
26484         this.progressDialog.render(Roo.get(document.body));
26485          
26486         this.progress = new Roo.bootstrap.Progress({
26487             cls : 'roo-document-manager-progress',
26488             active : true,
26489             striped : true
26490         });
26491         
26492         this.progress.render(this.progressDialog.getChildContainer());
26493         
26494         this.progressBar = new Roo.bootstrap.ProgressBar({
26495             cls : 'roo-document-manager-progress-bar',
26496             aria_valuenow : 0,
26497             aria_valuemin : 0,
26498             aria_valuemax : 12,
26499             panel : 'success'
26500         });
26501         
26502         this.progressBar.render(this.progress.getChildContainer());
26503     },
26504     
26505     onUploaderClick : function(e)
26506     {
26507         e.preventDefault();
26508      
26509         if(this.fireEvent('beforeselectfile', this) != false){
26510             this.selectorEl.dom.click();
26511         }
26512         
26513     },
26514     
26515     onFileSelected : function(e)
26516     {
26517         e.preventDefault();
26518         
26519         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26520             return;
26521         }
26522         
26523         Roo.each(this.selectorEl.dom.files, function(file){
26524             if(this.fireEvent('inspect', this, file) != false){
26525                 this.files.push(file);
26526             }
26527         }, this);
26528         
26529         this.queue();
26530         
26531     },
26532     
26533     queue : function()
26534     {
26535         this.selectorEl.dom.value = '';
26536         
26537         if(!this.files.length){
26538             return;
26539         }
26540         
26541         if(this.boxes > 0 && this.files.length > this.boxes){
26542             this.files = this.files.slice(0, this.boxes);
26543         }
26544         
26545         this.uploader.show();
26546         
26547         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26548             this.uploader.hide();
26549         }
26550         
26551         var _this = this;
26552         
26553         var files = [];
26554         
26555         var docs = [];
26556         
26557         Roo.each(this.files, function(file){
26558             
26559             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26560                 var f = this.renderPreview(file);
26561                 files.push(f);
26562                 return;
26563             }
26564             
26565             if(file.type.indexOf('image') != -1){
26566                 this.delegates.push(
26567                     (function(){
26568                         _this.process(file);
26569                     }).createDelegate(this)
26570                 );
26571         
26572                 return;
26573             }
26574             
26575             docs.push(
26576                 (function(){
26577                     _this.process(file);
26578                 }).createDelegate(this)
26579             );
26580             
26581         }, this);
26582         
26583         this.files = files;
26584         
26585         this.delegates = this.delegates.concat(docs);
26586         
26587         if(!this.delegates.length){
26588             this.refresh();
26589             return;
26590         }
26591         
26592         this.progressBar.aria_valuemax = this.delegates.length;
26593         
26594         this.arrange();
26595         
26596         return;
26597     },
26598     
26599     arrange : function()
26600     {
26601         if(!this.delegates.length){
26602             this.progressDialog.hide();
26603             this.refresh();
26604             return;
26605         }
26606         
26607         var delegate = this.delegates.shift();
26608         
26609         this.progressDialog.show();
26610         
26611         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26612         
26613         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26614         
26615         delegate();
26616     },
26617     
26618     refresh : function()
26619     {
26620         this.uploader.show();
26621         
26622         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26623             this.uploader.hide();
26624         }
26625         
26626         Roo.isTouch ? this.closable(false) : this.closable(true);
26627         
26628         this.fireEvent('refresh', this);
26629     },
26630     
26631     onRemove : function(e, el, o)
26632     {
26633         e.preventDefault();
26634         
26635         this.fireEvent('remove', this, o);
26636         
26637     },
26638     
26639     remove : function(o)
26640     {
26641         var files = [];
26642         
26643         Roo.each(this.files, function(file){
26644             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26645                 files.push(file);
26646                 return;
26647             }
26648
26649             o.target.remove();
26650
26651         }, this);
26652         
26653         this.files = files;
26654         
26655         this.refresh();
26656     },
26657     
26658     clear : function()
26659     {
26660         Roo.each(this.files, function(file){
26661             if(!file.target){
26662                 return;
26663             }
26664             
26665             file.target.remove();
26666
26667         }, this);
26668         
26669         this.files = [];
26670         
26671         this.refresh();
26672     },
26673     
26674     onClick : function(e, el, o)
26675     {
26676         e.preventDefault();
26677         
26678         this.fireEvent('click', this, o);
26679         
26680     },
26681     
26682     closable : function(closable)
26683     {
26684         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26685             
26686             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26687             
26688             if(closable){
26689                 el.show();
26690                 return;
26691             }
26692             
26693             el.hide();
26694             
26695         }, this);
26696     },
26697     
26698     xhrOnLoad : function(xhr)
26699     {
26700         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26701             el.remove();
26702         }, this);
26703         
26704         if (xhr.readyState !== 4) {
26705             this.arrange();
26706             this.fireEvent('exception', this, xhr);
26707             return;
26708         }
26709
26710         var response = Roo.decode(xhr.responseText);
26711         
26712         if(!response.success){
26713             this.arrange();
26714             this.fireEvent('exception', this, xhr);
26715             return;
26716         }
26717         
26718         var file = this.renderPreview(response.data);
26719         
26720         this.files.push(file);
26721         
26722         this.arrange();
26723         
26724     },
26725     
26726     xhrOnError : function()
26727     {
26728         Roo.log('xhr on error');
26729         
26730         var response = Roo.decode(xhr.responseText);
26731           
26732         Roo.log(response);
26733         
26734         this.arrange();
26735     },
26736     
26737     process : function(file)
26738     {
26739         if(this.fireEvent('process', this, file) !== false){
26740             if(this.editable && file.type.indexOf('image') != -1){
26741                 this.fireEvent('edit', this, file);
26742                 return;
26743             }
26744
26745             this.uploadStart(file, false);
26746
26747             return;
26748         }
26749         
26750     },
26751     
26752     uploadStart : function(file, crop)
26753     {
26754         this.xhr = new XMLHttpRequest();
26755         
26756         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26757             this.arrange();
26758             return;
26759         }
26760         
26761         file.xhr = this.xhr;
26762             
26763         this.managerEl.createChild({
26764             tag : 'div',
26765             cls : 'roo-document-manager-loading',
26766             cn : [
26767                 {
26768                     tag : 'div',
26769                     tooltip : file.name,
26770                     cls : 'roo-document-manager-thumb',
26771                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26772                 }
26773             ]
26774
26775         });
26776
26777         this.xhr.open(this.method, this.url, true);
26778         
26779         var headers = {
26780             "Accept": "application/json",
26781             "Cache-Control": "no-cache",
26782             "X-Requested-With": "XMLHttpRequest"
26783         };
26784         
26785         for (var headerName in headers) {
26786             var headerValue = headers[headerName];
26787             if (headerValue) {
26788                 this.xhr.setRequestHeader(headerName, headerValue);
26789             }
26790         }
26791         
26792         var _this = this;
26793         
26794         this.xhr.onload = function()
26795         {
26796             _this.xhrOnLoad(_this.xhr);
26797         }
26798         
26799         this.xhr.onerror = function()
26800         {
26801             _this.xhrOnError(_this.xhr);
26802         }
26803         
26804         var formData = new FormData();
26805
26806         formData.append('returnHTML', 'NO');
26807         
26808         if(crop){
26809             formData.append('crop', crop);
26810         }
26811         
26812         formData.append(this.paramName, file, file.name);
26813         
26814         if(this.fireEvent('prepare', this, formData) != false){
26815             this.xhr.send(formData);
26816         };
26817     },
26818     
26819     uploadCancel : function()
26820     {
26821         if (this.xhr) {
26822             this.xhr.abort();
26823         }
26824         
26825         
26826         this.delegates = [];
26827         
26828         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26829             el.remove();
26830         }, this);
26831         
26832         this.arrange();
26833     },
26834     
26835     renderPreview : function(file)
26836     {
26837         if(typeof(file.target) != 'undefined' && file.target){
26838             return file;
26839         }
26840         
26841         var previewEl = this.managerEl.createChild({
26842             tag : 'div',
26843             cls : 'roo-document-manager-preview',
26844             cn : [
26845                 {
26846                     tag : 'div',
26847                     tooltip : file.filename,
26848                     cls : 'roo-document-manager-thumb',
26849                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26850                 },
26851                 {
26852                     tag : 'button',
26853                     cls : 'close',
26854                     html : '<i class="fa fa-times-circle"></i>'
26855                 }
26856             ]
26857         });
26858
26859         var close = previewEl.select('button.close', true).first();
26860
26861         close.on('click', this.onRemove, this, file);
26862
26863         file.target = previewEl;
26864
26865         var image = previewEl.select('img', true).first();
26866         
26867         var _this = this;
26868         
26869         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26870         
26871         image.on('click', this.onClick, this, file);
26872         
26873         return file;
26874         
26875     },
26876     
26877     onPreviewLoad : function(file, image)
26878     {
26879         if(typeof(file.target) == 'undefined' || !file.target){
26880             return;
26881         }
26882         
26883         var width = image.dom.naturalWidth || image.dom.width;
26884         var height = image.dom.naturalHeight || image.dom.height;
26885         
26886         if(width > height){
26887             file.target.addClass('wide');
26888             return;
26889         }
26890         
26891         file.target.addClass('tall');
26892         return;
26893         
26894     },
26895     
26896     uploadFromSource : function(file, crop)
26897     {
26898         this.xhr = new XMLHttpRequest();
26899         
26900         this.managerEl.createChild({
26901             tag : 'div',
26902             cls : 'roo-document-manager-loading',
26903             cn : [
26904                 {
26905                     tag : 'div',
26906                     tooltip : file.name,
26907                     cls : 'roo-document-manager-thumb',
26908                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26909                 }
26910             ]
26911
26912         });
26913
26914         this.xhr.open(this.method, this.url, true);
26915         
26916         var headers = {
26917             "Accept": "application/json",
26918             "Cache-Control": "no-cache",
26919             "X-Requested-With": "XMLHttpRequest"
26920         };
26921         
26922         for (var headerName in headers) {
26923             var headerValue = headers[headerName];
26924             if (headerValue) {
26925                 this.xhr.setRequestHeader(headerName, headerValue);
26926             }
26927         }
26928         
26929         var _this = this;
26930         
26931         this.xhr.onload = function()
26932         {
26933             _this.xhrOnLoad(_this.xhr);
26934         }
26935         
26936         this.xhr.onerror = function()
26937         {
26938             _this.xhrOnError(_this.xhr);
26939         }
26940         
26941         var formData = new FormData();
26942
26943         formData.append('returnHTML', 'NO');
26944         
26945         formData.append('crop', crop);
26946         
26947         if(typeof(file.filename) != 'undefined'){
26948             formData.append('filename', file.filename);
26949         }
26950         
26951         if(typeof(file.mimetype) != 'undefined'){
26952             formData.append('mimetype', file.mimetype);
26953         }
26954         
26955         if(this.fireEvent('prepare', this, formData) != false){
26956             this.xhr.send(formData);
26957         };
26958     }
26959 });
26960
26961 /*
26962 * Licence: LGPL
26963 */
26964
26965 /**
26966  * @class Roo.bootstrap.DocumentViewer
26967  * @extends Roo.bootstrap.Component
26968  * Bootstrap DocumentViewer class
26969  * 
26970  * @constructor
26971  * Create a new DocumentViewer
26972  * @param {Object} config The config object
26973  */
26974
26975 Roo.bootstrap.DocumentViewer = function(config){
26976     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26977     
26978     this.addEvents({
26979         /**
26980          * @event initial
26981          * Fire after initEvent
26982          * @param {Roo.bootstrap.DocumentViewer} this
26983          */
26984         "initial" : true,
26985         /**
26986          * @event click
26987          * Fire after click
26988          * @param {Roo.bootstrap.DocumentViewer} this
26989          */
26990         "click" : true,
26991         /**
26992          * @event trash
26993          * Fire after trash button
26994          * @param {Roo.bootstrap.DocumentViewer} this
26995          */
26996         "trash" : true
26997         
26998     });
26999 };
27000
27001 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27002     
27003     getAutoCreate : function()
27004     {
27005         var cfg = {
27006             tag : 'div',
27007             cls : 'roo-document-viewer',
27008             cn : [
27009                 {
27010                     tag : 'div',
27011                     cls : 'roo-document-viewer-body',
27012                     cn : [
27013                         {
27014                             tag : 'div',
27015                             cls : 'roo-document-viewer-thumb',
27016                             cn : [
27017                                 {
27018                                     tag : 'img',
27019                                     cls : 'roo-document-viewer-image'
27020                                 }
27021                             ]
27022                         }
27023                     ]
27024                 },
27025                 {
27026                     tag : 'div',
27027                     cls : 'roo-document-viewer-footer',
27028                     cn : {
27029                         tag : 'div',
27030                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27031                         cn : [
27032                             {
27033                                 tag : 'div',
27034                                 cls : 'btn-group',
27035                                 cn : [
27036                                     {
27037                                         tag : 'button',
27038                                         cls : 'btn btn-default roo-document-viewer-trash',
27039                                         html : '<i class="fa fa-trash"></i>'
27040                                     }
27041                                 ]
27042                             }
27043                         ]
27044                     }
27045                 }
27046             ]
27047         };
27048         
27049         return cfg;
27050     },
27051     
27052     initEvents : function()
27053     {
27054         
27055         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27056         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27057         
27058         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27059         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27060         
27061         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27062         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27063         
27064         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27065         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27066         
27067         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27068         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069         
27070         this.bodyEl.on('click', this.onClick, this);
27071         
27072         this.trashBtn.on('click', this.onTrash, this);
27073         
27074     },
27075     
27076     initial : function()
27077     {
27078 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27079         
27080         
27081         this.fireEvent('initial', this);
27082         
27083     },
27084     
27085     onClick : function(e)
27086     {
27087         e.preventDefault();
27088         
27089         this.fireEvent('click', this);
27090     },
27091     
27092     onTrash : function(e)
27093     {
27094         e.preventDefault();
27095         
27096         this.fireEvent('trash', this);
27097     }
27098     
27099 });
27100 /*
27101  * - LGPL
27102  *
27103  * nav progress bar
27104  * 
27105  */
27106
27107 /**
27108  * @class Roo.bootstrap.NavProgressBar
27109  * @extends Roo.bootstrap.Component
27110  * Bootstrap NavProgressBar class
27111  * 
27112  * @constructor
27113  * Create a new nav progress bar
27114  * @param {Object} config The config object
27115  */
27116
27117 Roo.bootstrap.NavProgressBar = function(config){
27118     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27119
27120     this.bullets = this.bullets || [];
27121    
27122 //    Roo.bootstrap.NavProgressBar.register(this);
27123      this.addEvents({
27124         /**
27125              * @event changed
27126              * Fires when the active item changes
27127              * @param {Roo.bootstrap.NavProgressBar} this
27128              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27129              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27130          */
27131         'changed': true
27132      });
27133     
27134 };
27135
27136 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27137     
27138     bullets : [],
27139     barItems : [],
27140     
27141     getAutoCreate : function()
27142     {
27143         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27144         
27145         cfg = {
27146             tag : 'div',
27147             cls : 'roo-navigation-bar-group',
27148             cn : [
27149                 {
27150                     tag : 'div',
27151                     cls : 'roo-navigation-top-bar'
27152                 },
27153                 {
27154                     tag : 'div',
27155                     cls : 'roo-navigation-bullets-bar',
27156                     cn : [
27157                         {
27158                             tag : 'ul',
27159                             cls : 'roo-navigation-bar'
27160                         }
27161                     ]
27162                 },
27163                 
27164                 {
27165                     tag : 'div',
27166                     cls : 'roo-navigation-bottom-bar'
27167                 }
27168             ]
27169             
27170         };
27171         
27172         return cfg;
27173         
27174     },
27175     
27176     initEvents: function() 
27177     {
27178         
27179     },
27180     
27181     onRender : function(ct, position) 
27182     {
27183         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27184         
27185         if(this.bullets.length){
27186             Roo.each(this.bullets, function(b){
27187                this.addItem(b);
27188             }, this);
27189         }
27190         
27191         this.format();
27192         
27193     },
27194     
27195     addItem : function(cfg)
27196     {
27197         var item = new Roo.bootstrap.NavProgressItem(cfg);
27198         
27199         item.parentId = this.id;
27200         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27201         
27202         if(cfg.html){
27203             var top = new Roo.bootstrap.Element({
27204                 tag : 'div',
27205                 cls : 'roo-navigation-bar-text'
27206             });
27207             
27208             var bottom = new Roo.bootstrap.Element({
27209                 tag : 'div',
27210                 cls : 'roo-navigation-bar-text'
27211             });
27212             
27213             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27214             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27215             
27216             var topText = new Roo.bootstrap.Element({
27217                 tag : 'span',
27218                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27219             });
27220             
27221             var bottomText = new Roo.bootstrap.Element({
27222                 tag : 'span',
27223                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27224             });
27225             
27226             topText.onRender(top.el, null);
27227             bottomText.onRender(bottom.el, null);
27228             
27229             item.topEl = top;
27230             item.bottomEl = bottom;
27231         }
27232         
27233         this.barItems.push(item);
27234         
27235         return item;
27236     },
27237     
27238     getActive : function()
27239     {
27240         var active = false;
27241         
27242         Roo.each(this.barItems, function(v){
27243             
27244             if (!v.isActive()) {
27245                 return;
27246             }
27247             
27248             active = v;
27249             return false;
27250             
27251         });
27252         
27253         return active;
27254     },
27255     
27256     setActiveItem : function(item)
27257     {
27258         var prev = false;
27259         
27260         Roo.each(this.barItems, function(v){
27261             if (v.rid == item.rid) {
27262                 return ;
27263             }
27264             
27265             if (v.isActive()) {
27266                 v.setActive(false);
27267                 prev = v;
27268             }
27269         });
27270
27271         item.setActive(true);
27272         
27273         this.fireEvent('changed', this, item, prev);
27274     },
27275     
27276     getBarItem: function(rid)
27277     {
27278         var ret = false;
27279         
27280         Roo.each(this.barItems, function(e) {
27281             if (e.rid != rid) {
27282                 return;
27283             }
27284             
27285             ret =  e;
27286             return false;
27287         });
27288         
27289         return ret;
27290     },
27291     
27292     indexOfItem : function(item)
27293     {
27294         var index = false;
27295         
27296         Roo.each(this.barItems, function(v, i){
27297             
27298             if (v.rid != item.rid) {
27299                 return;
27300             }
27301             
27302             index = i;
27303             return false
27304         });
27305         
27306         return index;
27307     },
27308     
27309     setActiveNext : function()
27310     {
27311         var i = this.indexOfItem(this.getActive());
27312         
27313         if (i > this.barItems.length) {
27314             return;
27315         }
27316         
27317         this.setActiveItem(this.barItems[i+1]);
27318     },
27319     
27320     setActivePrev : function()
27321     {
27322         var i = this.indexOfItem(this.getActive());
27323         
27324         if (i  < 1) {
27325             return;
27326         }
27327         
27328         this.setActiveItem(this.barItems[i-1]);
27329     },
27330     
27331     format : function()
27332     {
27333         if(!this.barItems.length){
27334             return;
27335         }
27336      
27337         var width = 100 / this.barItems.length;
27338         
27339         Roo.each(this.barItems, function(i){
27340             i.el.setStyle('width', width + '%');
27341             i.topEl.el.setStyle('width', width + '%');
27342             i.bottomEl.el.setStyle('width', width + '%');
27343         }, this);
27344         
27345     }
27346     
27347 });
27348 /*
27349  * - LGPL
27350  *
27351  * Nav Progress Item
27352  * 
27353  */
27354
27355 /**
27356  * @class Roo.bootstrap.NavProgressItem
27357  * @extends Roo.bootstrap.Component
27358  * Bootstrap NavProgressItem class
27359  * @cfg {String} rid the reference id
27360  * @cfg {Boolean} active (true|false) Is item active default false
27361  * @cfg {Boolean} disabled (true|false) Is item active default false
27362  * @cfg {String} html
27363  * @cfg {String} position (top|bottom) text position default bottom
27364  * @cfg {String} icon show icon instead of number
27365  * 
27366  * @constructor
27367  * Create a new NavProgressItem
27368  * @param {Object} config The config object
27369  */
27370 Roo.bootstrap.NavProgressItem = function(config){
27371     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27372     this.addEvents({
27373         // raw events
27374         /**
27375          * @event click
27376          * The raw click event for the entire grid.
27377          * @param {Roo.bootstrap.NavProgressItem} this
27378          * @param {Roo.EventObject} e
27379          */
27380         "click" : true
27381     });
27382    
27383 };
27384
27385 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27386     
27387     rid : '',
27388     active : false,
27389     disabled : false,
27390     html : '',
27391     position : 'bottom',
27392     icon : false,
27393     
27394     getAutoCreate : function()
27395     {
27396         var iconCls = 'roo-navigation-bar-item-icon';
27397         
27398         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27399         
27400         var cfg = {
27401             tag: 'li',
27402             cls: 'roo-navigation-bar-item',
27403             cn : [
27404                 {
27405                     tag : 'i',
27406                     cls : iconCls
27407                 }
27408             ]
27409         };
27410         
27411         if(this.active){
27412             cfg.cls += ' active';
27413         }
27414         if(this.disabled){
27415             cfg.cls += ' disabled';
27416         }
27417         
27418         return cfg;
27419     },
27420     
27421     disable : function()
27422     {
27423         this.setDisabled(true);
27424     },
27425     
27426     enable : function()
27427     {
27428         this.setDisabled(false);
27429     },
27430     
27431     initEvents: function() 
27432     {
27433         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27434         
27435         this.iconEl.on('click', this.onClick, this);
27436     },
27437     
27438     onClick : function(e)
27439     {
27440         e.preventDefault();
27441         
27442         if(this.disabled){
27443             return;
27444         }
27445         
27446         if(this.fireEvent('click', this, e) === false){
27447             return;
27448         };
27449         
27450         this.parent().setActiveItem(this);
27451     },
27452     
27453     isActive: function () 
27454     {
27455         return this.active;
27456     },
27457     
27458     setActive : function(state)
27459     {
27460         if(this.active == state){
27461             return;
27462         }
27463         
27464         this.active = state;
27465         
27466         if (state) {
27467             this.el.addClass('active');
27468             return;
27469         }
27470         
27471         this.el.removeClass('active');
27472         
27473         return;
27474     },
27475     
27476     setDisabled : function(state)
27477     {
27478         if(this.disabled == state){
27479             return;
27480         }
27481         
27482         this.disabled = state;
27483         
27484         if (state) {
27485             this.el.addClass('disabled');
27486             return;
27487         }
27488         
27489         this.el.removeClass('disabled');
27490     },
27491     
27492     tooltipEl : function()
27493     {
27494         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27495     }
27496 });
27497  
27498
27499  /*
27500  * - LGPL
27501  *
27502  * FieldLabel
27503  * 
27504  */
27505
27506 /**
27507  * @class Roo.bootstrap.FieldLabel
27508  * @extends Roo.bootstrap.Component
27509  * Bootstrap FieldLabel class
27510  * @cfg {String} html contents of the element
27511  * @cfg {String} tag tag of the element default label
27512  * @cfg {String} cls class of the element
27513  * @cfg {String} target label target 
27514  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27515  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27516  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27517  * @cfg {String} iconTooltip default "This field is required"
27518  * 
27519  * @constructor
27520  * Create a new FieldLabel
27521  * @param {Object} config The config object
27522  */
27523
27524 Roo.bootstrap.FieldLabel = function(config){
27525     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27526     
27527     this.addEvents({
27528             /**
27529              * @event invalid
27530              * Fires after the field has been marked as invalid.
27531              * @param {Roo.form.FieldLabel} this
27532              * @param {String} msg The validation message
27533              */
27534             invalid : true,
27535             /**
27536              * @event valid
27537              * Fires after the field has been validated with no errors.
27538              * @param {Roo.form.FieldLabel} this
27539              */
27540             valid : true
27541         });
27542 };
27543
27544 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27545     
27546     tag: 'label',
27547     cls: '',
27548     html: '',
27549     target: '',
27550     allowBlank : true,
27551     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27552     validClass : 'text-success fa fa-lg fa-check',
27553     iconTooltip : 'This field is required',
27554     
27555     getAutoCreate : function(){
27556         
27557         var cfg = {
27558             tag : this.tag,
27559             cls : 'roo-bootstrap-field-label ' + this.cls,
27560             for : this.target,
27561             cn : [
27562                 {
27563                     tag : 'i',
27564                     cls : '',
27565                     tooltip : this.iconTooltip
27566                 },
27567                 {
27568                     tag : 'span',
27569                     html : this.html
27570                 }
27571             ] 
27572         };
27573         
27574         return cfg;
27575     },
27576     
27577     initEvents: function() 
27578     {
27579         Roo.bootstrap.Element.superclass.initEvents.call(this);
27580         
27581         this.iconEl = this.el.select('i', true).first();
27582         
27583         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27584         
27585         Roo.bootstrap.FieldLabel.register(this);
27586     },
27587     
27588     /**
27589      * Mark this field as valid
27590      */
27591     markValid : function()
27592     {
27593         this.iconEl.show();
27594         
27595         this.iconEl.removeClass(this.invalidClass);
27596         
27597         this.iconEl.addClass(this.validClass);
27598         
27599         this.fireEvent('valid', this);
27600     },
27601     
27602     /**
27603      * Mark this field as invalid
27604      * @param {String} msg The validation message
27605      */
27606     markInvalid : function(msg)
27607     {
27608         this.iconEl.show();
27609         
27610         this.iconEl.removeClass(this.validClass);
27611         
27612         this.iconEl.addClass(this.invalidClass);
27613         
27614         this.fireEvent('invalid', this, msg);
27615     }
27616     
27617    
27618 });
27619
27620 Roo.apply(Roo.bootstrap.FieldLabel, {
27621     
27622     groups: {},
27623     
27624      /**
27625     * register a FieldLabel Group
27626     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27627     */
27628     register : function(label)
27629     {
27630         if(this.groups.hasOwnProperty(label.target)){
27631             return;
27632         }
27633      
27634         this.groups[label.target] = label;
27635         
27636     },
27637     /**
27638     * fetch a FieldLabel Group based on the target
27639     * @param {string} target
27640     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27641     */
27642     get: function(target) {
27643         if (typeof(this.groups[target]) == 'undefined') {
27644             return false;
27645         }
27646         
27647         return this.groups[target] ;
27648     }
27649 });
27650
27651  
27652
27653  /*
27654  * - LGPL
27655  *
27656  * page DateSplitField.
27657  * 
27658  */
27659
27660
27661 /**
27662  * @class Roo.bootstrap.DateSplitField
27663  * @extends Roo.bootstrap.Component
27664  * Bootstrap DateSplitField class
27665  * @cfg {string} fieldLabel - the label associated
27666  * @cfg {Number} labelWidth set the width of label (0-12)
27667  * @cfg {String} labelAlign (top|left)
27668  * @cfg {Boolean} dayAllowBlank (true|false) default false
27669  * @cfg {Boolean} monthAllowBlank (true|false) default false
27670  * @cfg {Boolean} yearAllowBlank (true|false) default false
27671  * @cfg {string} dayPlaceholder 
27672  * @cfg {string} monthPlaceholder
27673  * @cfg {string} yearPlaceholder
27674  * @cfg {string} dayFormat default 'd'
27675  * @cfg {string} monthFormat default 'm'
27676  * @cfg {string} yearFormat default 'Y'
27677
27678  *     
27679  * @constructor
27680  * Create a new DateSplitField
27681  * @param {Object} config The config object
27682  */
27683
27684 Roo.bootstrap.DateSplitField = function(config){
27685     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27686     
27687     this.addEvents({
27688         // raw events
27689          /**
27690          * @event years
27691          * getting the data of years
27692          * @param {Roo.bootstrap.DateSplitField} this
27693          * @param {Object} years
27694          */
27695         "years" : true,
27696         /**
27697          * @event days
27698          * getting the data of days
27699          * @param {Roo.bootstrap.DateSplitField} this
27700          * @param {Object} days
27701          */
27702         "days" : true,
27703         /**
27704          * @event invalid
27705          * Fires after the field has been marked as invalid.
27706          * @param {Roo.form.Field} this
27707          * @param {String} msg The validation message
27708          */
27709         invalid : true,
27710        /**
27711          * @event valid
27712          * Fires after the field has been validated with no errors.
27713          * @param {Roo.form.Field} this
27714          */
27715         valid : true
27716     });
27717 };
27718
27719 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27720     
27721     fieldLabel : '',
27722     labelAlign : 'top',
27723     labelWidth : 3,
27724     dayAllowBlank : false,
27725     monthAllowBlank : false,
27726     yearAllowBlank : false,
27727     dayPlaceholder : '',
27728     monthPlaceholder : '',
27729     yearPlaceholder : '',
27730     dayFormat : 'd',
27731     monthFormat : 'm',
27732     yearFormat : 'Y',
27733     isFormField : true,
27734     
27735     getAutoCreate : function()
27736     {
27737         var cfg = {
27738             tag : 'div',
27739             cls : 'row roo-date-split-field-group',
27740             cn : [
27741                 {
27742                     tag : 'input',
27743                     type : 'hidden',
27744                     cls : 'form-hidden-field roo-date-split-field-group-value',
27745                     name : this.name
27746                 }
27747             ]
27748         };
27749         
27750         if(this.fieldLabel){
27751             cfg.cn.push({
27752                 tag : 'div',
27753                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27754                 cn : [
27755                     {
27756                         tag : 'label',
27757                         html : this.fieldLabel
27758                     }
27759                 ]
27760             });
27761         }
27762         
27763         Roo.each(['day', 'month', 'year'], function(t){
27764             cfg.cn.push({
27765                 tag : 'div',
27766                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27767             });
27768         }, this);
27769         
27770         return cfg;
27771     },
27772     
27773     inputEl: function ()
27774     {
27775         return this.el.select('.roo-date-split-field-group-value', true).first();
27776     },
27777     
27778     onRender : function(ct, position) 
27779     {
27780         var _this = this;
27781         
27782         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27783         
27784         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27785         
27786         this.dayField = new Roo.bootstrap.ComboBox({
27787             allowBlank : this.dayAllowBlank,
27788             alwaysQuery : true,
27789             displayField : 'value',
27790             editable : false,
27791             fieldLabel : '',
27792             forceSelection : true,
27793             mode : 'local',
27794             placeholder : this.dayPlaceholder,
27795             selectOnFocus : true,
27796             tpl : '<div class="select2-result"><b>{value}</b></div>',
27797             triggerAction : 'all',
27798             typeAhead : true,
27799             valueField : 'value',
27800             store : new Roo.data.SimpleStore({
27801                 data : (function() {    
27802                     var days = [];
27803                     _this.fireEvent('days', _this, days);
27804                     return days;
27805                 })(),
27806                 fields : [ 'value' ]
27807             }),
27808             listeners : {
27809                 select : function (_self, record, index)
27810                 {
27811                     _this.setValue(_this.getValue());
27812                 }
27813             }
27814         });
27815
27816         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27817         
27818         this.monthField = new Roo.bootstrap.MonthField({
27819             after : '<i class=\"fa fa-calendar\"></i>',
27820             allowBlank : this.monthAllowBlank,
27821             placeholder : this.monthPlaceholder,
27822             readOnly : true,
27823             listeners : {
27824                 render : function (_self)
27825                 {
27826                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27827                         e.preventDefault();
27828                         _self.focus();
27829                     });
27830                 },
27831                 select : function (_self, oldvalue, newvalue)
27832                 {
27833                     _this.setValue(_this.getValue());
27834                 }
27835             }
27836         });
27837         
27838         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27839         
27840         this.yearField = new Roo.bootstrap.ComboBox({
27841             allowBlank : this.yearAllowBlank,
27842             alwaysQuery : true,
27843             displayField : 'value',
27844             editable : false,
27845             fieldLabel : '',
27846             forceSelection : true,
27847             mode : 'local',
27848             placeholder : this.yearPlaceholder,
27849             selectOnFocus : true,
27850             tpl : '<div class="select2-result"><b>{value}</b></div>',
27851             triggerAction : 'all',
27852             typeAhead : true,
27853             valueField : 'value',
27854             store : new Roo.data.SimpleStore({
27855                 data : (function() {
27856                     var years = [];
27857                     _this.fireEvent('years', _this, years);
27858                     return years;
27859                 })(),
27860                 fields : [ 'value' ]
27861             }),
27862             listeners : {
27863                 select : function (_self, record, index)
27864                 {
27865                     _this.setValue(_this.getValue());
27866                 }
27867             }
27868         });
27869
27870         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27871     },
27872     
27873     setValue : function(v, format)
27874     {
27875         this.inputEl.dom.value = v;
27876         
27877         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27878         
27879         var d = Date.parseDate(v, f);
27880         
27881         if(!d){
27882             this.validate();
27883             return;
27884         }
27885         
27886         this.setDay(d.format(this.dayFormat));
27887         this.setMonth(d.format(this.monthFormat));
27888         this.setYear(d.format(this.yearFormat));
27889         
27890         this.validate();
27891         
27892         return;
27893     },
27894     
27895     setDay : function(v)
27896     {
27897         this.dayField.setValue(v);
27898         this.inputEl.dom.value = this.getValue();
27899         this.validate();
27900         return;
27901     },
27902     
27903     setMonth : function(v)
27904     {
27905         this.monthField.setValue(v, true);
27906         this.inputEl.dom.value = this.getValue();
27907         this.validate();
27908         return;
27909     },
27910     
27911     setYear : function(v)
27912     {
27913         this.yearField.setValue(v);
27914         this.inputEl.dom.value = this.getValue();
27915         this.validate();
27916         return;
27917     },
27918     
27919     getDay : function()
27920     {
27921         return this.dayField.getValue();
27922     },
27923     
27924     getMonth : function()
27925     {
27926         return this.monthField.getValue();
27927     },
27928     
27929     getYear : function()
27930     {
27931         return this.yearField.getValue();
27932     },
27933     
27934     getValue : function()
27935     {
27936         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27937         
27938         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27939         
27940         return date;
27941     },
27942     
27943     reset : function()
27944     {
27945         this.setDay('');
27946         this.setMonth('');
27947         this.setYear('');
27948         this.inputEl.dom.value = '';
27949         this.validate();
27950         return;
27951     },
27952     
27953     validate : function()
27954     {
27955         var d = this.dayField.validate();
27956         var m = this.monthField.validate();
27957         var y = this.yearField.validate();
27958         
27959         var valid = true;
27960         
27961         if(
27962                 (!this.dayAllowBlank && !d) ||
27963                 (!this.monthAllowBlank && !m) ||
27964                 (!this.yearAllowBlank && !y)
27965         ){
27966             valid = false;
27967         }
27968         
27969         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27970             return valid;
27971         }
27972         
27973         if(valid){
27974             this.markValid();
27975             return valid;
27976         }
27977         
27978         this.markInvalid();
27979         
27980         return valid;
27981     },
27982     
27983     markValid : function()
27984     {
27985         
27986         var label = this.el.select('label', true).first();
27987         var icon = this.el.select('i.fa-star', true).first();
27988
27989         if(label && icon){
27990             icon.remove();
27991         }
27992         
27993         this.fireEvent('valid', this);
27994     },
27995     
27996      /**
27997      * Mark this field as invalid
27998      * @param {String} msg The validation message
27999      */
28000     markInvalid : function(msg)
28001     {
28002         
28003         var label = this.el.select('label', true).first();
28004         var icon = this.el.select('i.fa-star', true).first();
28005
28006         if(label && !icon){
28007             this.el.select('.roo-date-split-field-label', true).createChild({
28008                 tag : 'i',
28009                 cls : 'text-danger fa fa-lg fa-star',
28010                 tooltip : 'This field is required',
28011                 style : 'margin-right:5px;'
28012             }, label, true);
28013         }
28014         
28015         this.fireEvent('invalid', this, msg);
28016     },
28017     
28018     clearInvalid : function()
28019     {
28020         var label = this.el.select('label', true).first();
28021         var icon = this.el.select('i.fa-star', true).first();
28022
28023         if(label && icon){
28024             icon.remove();
28025         }
28026         
28027         this.fireEvent('valid', this);
28028     },
28029     
28030     getName: function()
28031     {
28032         return this.name;
28033     }
28034     
28035 });
28036
28037