roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         };
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             };
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994  * @cfg {Boolean} clickable (true|false) default false
995
996  *     
997  * @constructor
998  * Create a new Container
999  * @param {Object} config The config object
1000  */
1001
1002 Roo.bootstrap.Container = function(config){
1003     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1004     
1005     this.addEvents({
1006         // raw events
1007          /**
1008          * @event expand
1009          * After the panel has been expand
1010          * 
1011          * @param {Roo.bootstrap.Container} this
1012          */
1013         "expand" : true,
1014         /**
1015          * @event collapse
1016          * After the panel has been collapsed
1017          * 
1018          * @param {Roo.bootstrap.Container} this
1019          */
1020         "collapse" : true,
1021         /**
1022          * @event click
1023          * When a element is chick
1024          * @param {Roo.bootstrap.Container} this
1025          * @param {Roo.EventObject} e
1026          */
1027         "click" : true
1028     });
1029 };
1030
1031 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1032     
1033     jumbotron : false,
1034     well: '',
1035     panel : '',
1036     header: '',
1037     footer : '',
1038     sticky: '',
1039     tag : false,
1040     alert : false,
1041     fa: false,
1042     icon : false,
1043     expandable : false,
1044     rheader : '',
1045     expanded : true,
1046     clickable: false,
1047   
1048      
1049     getChildContainer : function() {
1050         
1051         if(!this.el){
1052             return false;
1053         }
1054         
1055         if (this.panel.length) {
1056             return this.el.select('.panel-body',true).first();
1057         }
1058         
1059         return this.el;
1060     },
1061     
1062     
1063     getAutoCreate : function(){
1064         
1065         var cfg = {
1066             tag : this.tag || 'div',
1067             html : '',
1068             cls : ''
1069         };
1070         if (this.jumbotron) {
1071             cfg.cls = 'jumbotron';
1072         }
1073         
1074         
1075         
1076         // - this is applied by the parent..
1077         //if (this.cls) {
1078         //    cfg.cls = this.cls + '';
1079         //}
1080         
1081         if (this.sticky.length) {
1082             
1083             var bd = Roo.get(document.body);
1084             if (!bd.hasClass('bootstrap-sticky')) {
1085                 bd.addClass('bootstrap-sticky');
1086                 Roo.select('html',true).setStyle('height', '100%');
1087             }
1088              
1089             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1090         }
1091         
1092         
1093         if (this.well.length) {
1094             switch (this.well) {
1095                 case 'lg':
1096                 case 'sm':
1097                     cfg.cls +=' well well-' +this.well;
1098                     break;
1099                 default:
1100                     cfg.cls +=' well';
1101                     break;
1102             }
1103         }
1104         
1105         if (this.hidden) {
1106             cfg.cls += ' hidden';
1107         }
1108         
1109         
1110         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1111             cfg.cls +=' alert alert-' + this.alert;
1112         }
1113         
1114         var body = cfg;
1115         
1116         if (this.panel.length) {
1117             cfg.cls += ' panel panel-' + this.panel;
1118             cfg.cn = [];
1119             if (this.header.length) {
1120                 
1121                 var h = [];
1122                 
1123                 if(this.expandable){
1124                     
1125                     cfg.cls = cfg.cls + ' expandable';
1126                     
1127                     h.push({
1128                         tag: 'i',
1129                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1130                     });
1131                     
1132                 }
1133                 
1134                 h.push(
1135                     {
1136                         tag: 'span',
1137                         cls : 'panel-title',
1138                         html : (this.expandable ? '&nbsp;' : '') + this.header
1139                     },
1140                     {
1141                         tag: 'span',
1142                         cls: 'panel-header-right',
1143                         html: this.rheader
1144                     }
1145                 );
1146                 
1147                 cfg.cn.push({
1148                     cls : 'panel-heading',
1149                     style : this.expandable ? 'cursor: pointer' : '',
1150                     cn : h
1151                 });
1152                 
1153             }
1154             
1155             body = false;
1156             cfg.cn.push({
1157                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1158                 html : this.html
1159             });
1160             
1161             
1162             if (this.footer.length) {
1163                 cfg.cn.push({
1164                     cls : 'panel-footer',
1165                     html : this.footer
1166                     
1167                 });
1168             }
1169             
1170         }
1171         
1172         if (body) {
1173             body.html = this.html || cfg.html;
1174             // prefix with the icons..
1175             if (this.fa) {
1176                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1177             }
1178             if (this.icon) {
1179                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1180             }
1181             
1182             
1183         }
1184         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1185             cfg.cls =  'container';
1186         }
1187         
1188         return cfg;
1189     },
1190     
1191     initEvents: function() 
1192     {
1193         if(this.expandable){
1194             var headerEl = this.headerEl();
1195         
1196             if(headerEl){
1197                 headerEl.on('click', this.onToggleClick, this);
1198             }
1199         }
1200         
1201         if(this.clickable){
1202             this.el.on('click', this.onClick, this);
1203         }
1204         
1205     },
1206     
1207     onToggleClick : function()
1208     {
1209         var headerEl = this.headerEl();
1210         
1211         if(!headerEl){
1212             return;
1213         }
1214         
1215         if(this.expanded){
1216             this.collapse();
1217             return;
1218         }
1219         
1220         this.expand();
1221     },
1222     
1223     expand : function()
1224     {
1225         if(this.fireEvent('expand', this)) {
1226             
1227             this.expanded = true;
1228             
1229             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1230             
1231             this.el.select('.panel-body',true).first().removeClass('hide');
1232             
1233             var toggleEl = this.toggleEl();
1234
1235             if(!toggleEl){
1236                 return;
1237             }
1238
1239             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1240         }
1241         
1242     },
1243     
1244     collapse : function()
1245     {
1246         if(this.fireEvent('collapse', this)) {
1247             
1248             this.expanded = false;
1249             
1250             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1251             this.el.select('.panel-body',true).first().addClass('hide');
1252         
1253             var toggleEl = this.toggleEl();
1254
1255             if(!toggleEl){
1256                 return;
1257             }
1258
1259             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1260         }
1261     },
1262     
1263     toggleEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading .fa',true).first();
1270     },
1271     
1272     headerEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-heading',true).first()
1279     },
1280     
1281     titleEl : function()
1282     {
1283         if(!this.el || !this.panel.length || !this.header.length){
1284             return;
1285         }
1286         
1287         return this.el.select('.panel-title',true).first();
1288     },
1289     
1290     setTitle : function(v)
1291     {
1292         var titleEl = this.titleEl();
1293         
1294         if(!titleEl){
1295             return;
1296         }
1297         
1298         titleEl.dom.innerHTML = v;
1299     },
1300     
1301     getTitle : function()
1302     {
1303         
1304         var titleEl = this.titleEl();
1305         
1306         if(!titleEl){
1307             return '';
1308         }
1309         
1310         return titleEl.dom.innerHTML;
1311     },
1312     
1313     setRightTitle : function(v)
1314     {
1315         var t = this.el.select('.panel-header-right',true).first();
1316         
1317         if(!t){
1318             return;
1319         }
1320         
1321         t.dom.innerHTML = v;
1322     },
1323     
1324     onClick : function(e)
1325     {
1326         e.preventDefault();
1327         
1328         this.fireEvent('click', this, e);
1329     }
1330    
1331 });
1332
1333  /*
1334  * - LGPL
1335  *
1336  * image
1337  * 
1338  */
1339
1340
1341 /**
1342  * @class Roo.bootstrap.Img
1343  * @extends Roo.bootstrap.Component
1344  * Bootstrap Img class
1345  * @cfg {Boolean} imgResponsive false | true
1346  * @cfg {String} border rounded | circle | thumbnail
1347  * @cfg {String} src image source
1348  * @cfg {String} alt image alternative text
1349  * @cfg {String} href a tag href
1350  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1351  * @cfg {String} xsUrl xs image source
1352  * @cfg {String} smUrl sm image source
1353  * @cfg {String} mdUrl md image source
1354  * @cfg {String} lgUrl lg image source
1355  * 
1356  * @constructor
1357  * Create a new Input
1358  * @param {Object} config The config object
1359  */
1360
1361 Roo.bootstrap.Img = function(config){
1362     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1363     
1364     this.addEvents({
1365         // img events
1366         /**
1367          * @event click
1368          * The img click event for the img.
1369          * @param {Roo.EventObject} e
1370          */
1371         "click" : true
1372     });
1373 };
1374
1375 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1376     
1377     imgResponsive: true,
1378     border: '',
1379     src: 'about:blank',
1380     href: false,
1381     target: false,
1382     xsUrl: '',
1383     smUrl: '',
1384     mdUrl: '',
1385     lgUrl: '',
1386
1387     getAutoCreate : function()
1388     {   
1389         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1390             return this.createSingleImg();
1391         }
1392         
1393         var cfg = {
1394             tag: 'div',
1395             cls: 'roo-image-responsive-group',
1396             cn: []
1397         };
1398         var _this = this;
1399         
1400         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1401             
1402             if(!_this[size + 'Url']){
1403                 return;
1404             }
1405             
1406             var img = {
1407                 tag: 'img',
1408                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1409                 html: _this.html || cfg.html,
1410                 src: _this[size + 'Url']
1411             };
1412             
1413             img.cls += ' roo-image-responsive-' + size;
1414             
1415             var s = ['xs', 'sm', 'md', 'lg'];
1416             
1417             s.splice(s.indexOf(size), 1);
1418             
1419             Roo.each(s, function(ss){
1420                 img.cls += ' hidden-' + ss;
1421             });
1422             
1423             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1424                 cfg.cls += ' img-' + _this.border;
1425             }
1426             
1427             if(_this.alt){
1428                 cfg.alt = _this.alt;
1429             }
1430             
1431             if(_this.href){
1432                 var a = {
1433                     tag: 'a',
1434                     href: _this.href,
1435                     cn: [
1436                         img
1437                     ]
1438                 };
1439
1440                 if(this.target){
1441                     a.target = _this.target;
1442                 }
1443             }
1444             
1445             cfg.cn.push((_this.href) ? a : img);
1446             
1447         });
1448         
1449         return cfg;
1450     },
1451     
1452     createSingleImg : function()
1453     {
1454         var cfg = {
1455             tag: 'img',
1456             cls: (this.imgResponsive) ? 'img-responsive' : '',
1457             html : null,
1458             src : 'about:blank'  // just incase src get's set to undefined?!?
1459         };
1460         
1461         cfg.html = this.html || cfg.html;
1462         
1463         cfg.src = this.src || cfg.src;
1464         
1465         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1466             cfg.cls += ' img-' + this.border;
1467         }
1468         
1469         if(this.alt){
1470             cfg.alt = this.alt;
1471         }
1472         
1473         if(this.href){
1474             var a = {
1475                 tag: 'a',
1476                 href: this.href,
1477                 cn: [
1478                     cfg
1479                 ]
1480             };
1481             
1482             if(this.target){
1483                 a.target = this.target;
1484             }
1485             
1486         }
1487         
1488         return (this.href) ? a : cfg;
1489     },
1490     
1491     initEvents: function() 
1492     {
1493         if(!this.href){
1494             this.el.on('click', this.onClick, this);
1495         }
1496         
1497     },
1498     
1499     onClick : function(e)
1500     {
1501         Roo.log('img onclick');
1502         this.fireEvent('click', this, e);
1503     }
1504    
1505 });
1506
1507  /*
1508  * - LGPL
1509  *
1510  * image
1511  * 
1512  */
1513
1514
1515 /**
1516  * @class Roo.bootstrap.Link
1517  * @extends Roo.bootstrap.Component
1518  * Bootstrap Link Class
1519  * @cfg {String} alt image alternative text
1520  * @cfg {String} href a tag href
1521  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1522  * @cfg {String} html the content of the link.
1523  * @cfg {String} anchor name for the anchor link
1524
1525  * @cfg {Boolean} preventDefault (true | false) default false
1526
1527  * 
1528  * @constructor
1529  * Create a new Input
1530  * @param {Object} config The config object
1531  */
1532
1533 Roo.bootstrap.Link = function(config){
1534     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1535     
1536     this.addEvents({
1537         // img events
1538         /**
1539          * @event click
1540          * The img click event for the img.
1541          * @param {Roo.EventObject} e
1542          */
1543         "click" : true
1544     });
1545 };
1546
1547 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1548     
1549     href: false,
1550     target: false,
1551     preventDefault: false,
1552     anchor : false,
1553     alt : false,
1554
1555     getAutoCreate : function()
1556     {
1557         
1558         var cfg = {
1559             tag: 'a'
1560         };
1561         // anchor's do not require html/href...
1562         if (this.anchor === false) {
1563             cfg.html = this.html || '';
1564             cfg.href = this.href || '#';
1565         } else {
1566             cfg.name = this.anchor;
1567             if (this.html !== false) {
1568                 cfg.html = this.html;
1569             }
1570             if (this.href !== false) {
1571                 cfg.href = this.href;
1572             }
1573         }
1574         
1575         if(this.alt !== false){
1576             cfg.alt = this.alt;
1577         }
1578         
1579         
1580         if(this.target !== false) {
1581             cfg.target = this.target;
1582         }
1583         
1584         return cfg;
1585     },
1586     
1587     initEvents: function() {
1588         
1589         if(!this.href || this.preventDefault){
1590             this.el.on('click', this.onClick, this);
1591         }
1592     },
1593     
1594     onClick : function(e)
1595     {
1596         if(this.preventDefault){
1597             e.preventDefault();
1598         }
1599         //Roo.log('img onclick');
1600         this.fireEvent('click', this, e);
1601     }
1602    
1603 });
1604
1605  /*
1606  * - LGPL
1607  *
1608  * header
1609  * 
1610  */
1611
1612 /**
1613  * @class Roo.bootstrap.Header
1614  * @extends Roo.bootstrap.Component
1615  * Bootstrap Header class
1616  * @cfg {String} html content of header
1617  * @cfg {Number} level (1|2|3|4|5|6) default 1
1618  * 
1619  * @constructor
1620  * Create a new Header
1621  * @param {Object} config The config object
1622  */
1623
1624
1625 Roo.bootstrap.Header  = function(config){
1626     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1627 };
1628
1629 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1630     
1631     //href : false,
1632     html : false,
1633     level : 1,
1634     
1635     
1636     
1637     getAutoCreate : function(){
1638         
1639         
1640         
1641         var cfg = {
1642             tag: 'h' + (1 *this.level),
1643             html: this.html || ''
1644         } ;
1645         
1646         return cfg;
1647     }
1648    
1649 });
1650
1651  
1652
1653  /*
1654  * Based on:
1655  * Ext JS Library 1.1.1
1656  * Copyright(c) 2006-2007, Ext JS, LLC.
1657  *
1658  * Originally Released Under LGPL - original licence link has changed is not relivant.
1659  *
1660  * Fork - LGPL
1661  * <script type="text/javascript">
1662  */
1663  
1664 /**
1665  * @class Roo.bootstrap.MenuMgr
1666  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1667  * @singleton
1668  */
1669 Roo.bootstrap.MenuMgr = function(){
1670    var menus, active, groups = {}, attached = false, lastShow = new Date();
1671
1672    // private - called when first menu is created
1673    function init(){
1674        menus = {};
1675        active = new Roo.util.MixedCollection();
1676        Roo.get(document).addKeyListener(27, function(){
1677            if(active.length > 0){
1678                hideAll();
1679            }
1680        });
1681    }
1682
1683    // private
1684    function hideAll(){
1685        if(active && active.length > 0){
1686            var c = active.clone();
1687            c.each(function(m){
1688                m.hide();
1689            });
1690        }
1691    }
1692
1693    // private
1694    function onHide(m){
1695        active.remove(m);
1696        if(active.length < 1){
1697            Roo.get(document).un("mouseup", onMouseDown);
1698             
1699            attached = false;
1700        }
1701    }
1702
1703    // private
1704    function onShow(m){
1705        var last = active.last();
1706        lastShow = new Date();
1707        active.add(m);
1708        if(!attached){
1709           Roo.get(document).on("mouseup", onMouseDown);
1710            
1711            attached = true;
1712        }
1713        if(m.parentMenu){
1714           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1715           m.parentMenu.activeChild = m;
1716        }else if(last && last.isVisible()){
1717           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1718        }
1719    }
1720
1721    // private
1722    function onBeforeHide(m){
1723        if(m.activeChild){
1724            m.activeChild.hide();
1725        }
1726        if(m.autoHideTimer){
1727            clearTimeout(m.autoHideTimer);
1728            delete m.autoHideTimer;
1729        }
1730    }
1731
1732    // private
1733    function onBeforeShow(m){
1734        var pm = m.parentMenu;
1735        if(!pm && !m.allowOtherMenus){
1736            hideAll();
1737        }else if(pm && pm.activeChild && active != m){
1738            pm.activeChild.hide();
1739        }
1740    }
1741
1742    // private this should really trigger on mouseup..
1743    function onMouseDown(e){
1744         Roo.log("on Mouse Up");
1745         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1746             Roo.log("hideAll");
1747             hideAll();
1748             e.stopEvent();
1749         }
1750         
1751         
1752    }
1753
1754    // private
1755    function onBeforeCheck(mi, state){
1756        if(state){
1757            var g = groups[mi.group];
1758            for(var i = 0, l = g.length; i < l; i++){
1759                if(g[i] != mi){
1760                    g[i].setChecked(false);
1761                }
1762            }
1763        }
1764    }
1765
1766    return {
1767
1768        /**
1769         * Hides all menus that are currently visible
1770         */
1771        hideAll : function(){
1772             hideAll();  
1773        },
1774
1775        // private
1776        register : function(menu){
1777            if(!menus){
1778                init();
1779            }
1780            menus[menu.id] = menu;
1781            menu.on("beforehide", onBeforeHide);
1782            menu.on("hide", onHide);
1783            menu.on("beforeshow", onBeforeShow);
1784            menu.on("show", onShow);
1785            var g = menu.group;
1786            if(g && menu.events["checkchange"]){
1787                if(!groups[g]){
1788                    groups[g] = [];
1789                }
1790                groups[g].push(menu);
1791                menu.on("checkchange", onCheck);
1792            }
1793        },
1794
1795         /**
1796          * Returns a {@link Roo.menu.Menu} object
1797          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1798          * be used to generate and return a new Menu instance.
1799          */
1800        get : function(menu){
1801            if(typeof menu == "string"){ // menu id
1802                return menus[menu];
1803            }else if(menu.events){  // menu instance
1804                return menu;
1805            }
1806            /*else if(typeof menu.length == 'number'){ // array of menu items?
1807                return new Roo.bootstrap.Menu({items:menu});
1808            }else{ // otherwise, must be a config
1809                return new Roo.bootstrap.Menu(menu);
1810            }
1811            */
1812            return false;
1813        },
1814
1815        // private
1816        unregister : function(menu){
1817            delete menus[menu.id];
1818            menu.un("beforehide", onBeforeHide);
1819            menu.un("hide", onHide);
1820            menu.un("beforeshow", onBeforeShow);
1821            menu.un("show", onShow);
1822            var g = menu.group;
1823            if(g && menu.events["checkchange"]){
1824                groups[g].remove(menu);
1825                menu.un("checkchange", onCheck);
1826            }
1827        },
1828
1829        // private
1830        registerCheckable : function(menuItem){
1831            var g = menuItem.group;
1832            if(g){
1833                if(!groups[g]){
1834                    groups[g] = [];
1835                }
1836                groups[g].push(menuItem);
1837                menuItem.on("beforecheckchange", onBeforeCheck);
1838            }
1839        },
1840
1841        // private
1842        unregisterCheckable : function(menuItem){
1843            var g = menuItem.group;
1844            if(g){
1845                groups[g].remove(menuItem);
1846                menuItem.un("beforecheckchange", onBeforeCheck);
1847            }
1848        }
1849    };
1850 }();/*
1851  * - LGPL
1852  *
1853  * menu
1854  * 
1855  */
1856
1857 /**
1858  * @class Roo.bootstrap.Menu
1859  * @extends Roo.bootstrap.Component
1860  * Bootstrap Menu class - container for MenuItems
1861  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1862  * 
1863  * @constructor
1864  * Create a new Menu
1865  * @param {Object} config The config object
1866  */
1867
1868
1869 Roo.bootstrap.Menu = function(config){
1870     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1871     if (this.registerMenu) {
1872         Roo.bootstrap.MenuMgr.register(this);
1873     }
1874     this.addEvents({
1875         /**
1876          * @event beforeshow
1877          * Fires before this menu is displayed
1878          * @param {Roo.menu.Menu} this
1879          */
1880         beforeshow : true,
1881         /**
1882          * @event beforehide
1883          * Fires before this menu is hidden
1884          * @param {Roo.menu.Menu} this
1885          */
1886         beforehide : true,
1887         /**
1888          * @event show
1889          * Fires after this menu is displayed
1890          * @param {Roo.menu.Menu} this
1891          */
1892         show : true,
1893         /**
1894          * @event hide
1895          * Fires after this menu is hidden
1896          * @param {Roo.menu.Menu} this
1897          */
1898         hide : true,
1899         /**
1900          * @event click
1901          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1902          * @param {Roo.menu.Menu} this
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          * @param {Roo.EventObject} e
1905          */
1906         click : true,
1907         /**
1908          * @event mouseover
1909          * Fires when the mouse is hovering over this menu
1910          * @param {Roo.menu.Menu} this
1911          * @param {Roo.EventObject} e
1912          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1913          */
1914         mouseover : true,
1915         /**
1916          * @event mouseout
1917          * Fires when the mouse exits this menu
1918          * @param {Roo.menu.Menu} this
1919          * @param {Roo.EventObject} e
1920          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1921          */
1922         mouseout : true,
1923         /**
1924          * @event itemclick
1925          * Fires when a menu item contained in this menu is clicked
1926          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1927          * @param {Roo.EventObject} e
1928          */
1929         itemclick: true
1930     });
1931     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1935     
1936    /// html : false,
1937     //align : '',
1938     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1939     type: false,
1940     /**
1941      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1942      */
1943     registerMenu : true,
1944     
1945     menuItems :false, // stores the menu items..
1946     
1947     hidden:true,
1948     
1949     parentMenu : false,
1950     
1951     getChildContainer : function() {
1952         return this.el;  
1953     },
1954     
1955     getAutoCreate : function(){
1956          
1957         //if (['right'].indexOf(this.align)!==-1) {
1958         //    cfg.cn[1].cls += ' pull-right'
1959         //}
1960         
1961         
1962         var cfg = {
1963             tag : 'ul',
1964             cls : 'dropdown-menu' ,
1965             style : 'z-index:1000'
1966             
1967         };
1968         
1969         if (this.type === 'submenu') {
1970             cfg.cls = 'submenu active';
1971         }
1972         if (this.type === 'treeview') {
1973             cfg.cls = 'treeview-menu';
1974         }
1975         
1976         return cfg;
1977     },
1978     initEvents : function() {
1979         
1980        // Roo.log("ADD event");
1981        // Roo.log(this.triggerEl.dom);
1982         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1983         
1984         this.triggerEl.addClass('dropdown-toggle');
1985         
1986         if (Roo.isTouch) {
1987             this.el.on('touchstart'  , this.onTouch, this);
1988         }
1989         this.el.on('click' , this.onClick, this);
1990
1991         this.el.on("mouseover", this.onMouseOver, this);
1992         this.el.on("mouseout", this.onMouseOut, this);
1993         
1994     },
1995     
1996     findTargetItem : function(e)
1997     {
1998         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1999         if(!t){
2000             return false;
2001         }
2002         //Roo.log(t);         Roo.log(t.id);
2003         if(t && t.id){
2004             //Roo.log(this.menuitems);
2005             return this.menuitems.get(t.id);
2006             
2007             //return this.items.get(t.menuItemId);
2008         }
2009         
2010         return false;
2011     },
2012     
2013     onTouch : function(e) 
2014     {
2015         //e.stopEvent(); this make the user popdown broken
2016         this.onClick(e);
2017     },
2018     
2019     onClick : function(e)
2020     {
2021         Roo.log("menu.onClick");
2022         var t = this.findTargetItem(e);
2023         if(!t || t.isContainer){
2024             return;
2025         }
2026         Roo.log(e);
2027         /*
2028         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2029             if(t == this.activeItem && t.shouldDeactivate(e)){
2030                 this.activeItem.deactivate();
2031                 delete this.activeItem;
2032                 return;
2033             }
2034             if(t.canActivate){
2035                 this.setActiveItem(t, true);
2036             }
2037             return;
2038             
2039             
2040         }
2041         */
2042        
2043         Roo.log('pass click event');
2044         
2045         t.onClick(e);
2046         
2047         this.fireEvent("click", this, t, e);
2048         
2049         this.hide();
2050     },
2051      onMouseOver : function(e){
2052         var t  = this.findTargetItem(e);
2053         //Roo.log(t);
2054         //if(t){
2055         //    if(t.canActivate && !t.disabled){
2056         //        this.setActiveItem(t, true);
2057         //    }
2058         //}
2059         
2060         this.fireEvent("mouseover", this, e, t);
2061     },
2062     isVisible : function(){
2063         return !this.hidden;
2064     },
2065      onMouseOut : function(e){
2066         var t  = this.findTargetItem(e);
2067         
2068         //if(t ){
2069         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2070         //        this.activeItem.deactivate();
2071         //        delete this.activeItem;
2072         //    }
2073         //}
2074         this.fireEvent("mouseout", this, e, t);
2075     },
2076     
2077     
2078     /**
2079      * Displays this menu relative to another element
2080      * @param {String/HTMLElement/Roo.Element} element The element to align to
2081      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2082      * the element (defaults to this.defaultAlign)
2083      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2084      */
2085     show : function(el, pos, parentMenu){
2086         this.parentMenu = parentMenu;
2087         if(!this.el){
2088             this.render();
2089         }
2090         this.fireEvent("beforeshow", this);
2091         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2092     },
2093      /**
2094      * Displays this menu at a specific xy position
2095      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2096      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2097      */
2098     showAt : function(xy, parentMenu, /* private: */_e){
2099         this.parentMenu = parentMenu;
2100         if(!this.el){
2101             this.render();
2102         }
2103         if(_e !== false){
2104             this.fireEvent("beforeshow", this);
2105             //xy = this.el.adjustForConstraints(xy);
2106         }
2107         
2108         //this.el.show();
2109         this.hideMenuItems();
2110         this.hidden = false;
2111         this.triggerEl.addClass('open');
2112         
2113         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2114             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2115         }
2116         
2117         this.el.setXY(xy);
2118         this.focus();
2119         this.fireEvent("show", this);
2120     },
2121     
2122     focus : function(){
2123         return;
2124         if(!this.hidden){
2125             this.doFocus.defer(50, this);
2126         }
2127     },
2128
2129     doFocus : function(){
2130         if(!this.hidden){
2131             this.focusEl.focus();
2132         }
2133     },
2134
2135     /**
2136      * Hides this menu and optionally all parent menus
2137      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2138      */
2139     hide : function(deep){
2140         
2141         this.hideMenuItems();
2142         if(this.el && this.isVisible()){
2143             this.fireEvent("beforehide", this);
2144             if(this.activeItem){
2145                 this.activeItem.deactivate();
2146                 this.activeItem = null;
2147             }
2148             this.triggerEl.removeClass('open');;
2149             this.hidden = true;
2150             this.fireEvent("hide", this);
2151         }
2152         if(deep === true && this.parentMenu){
2153             this.parentMenu.hide(true);
2154         }
2155     },
2156     
2157     onTriggerPress  : function(e)
2158     {
2159         
2160         Roo.log('trigger press');
2161         //Roo.log(e.getTarget());
2162        // Roo.log(this.triggerEl.dom);
2163         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2164             return;
2165         }
2166         
2167         if (this.isVisible()) {
2168             Roo.log('hide');
2169             this.hide();
2170         } else {
2171             Roo.log('show');
2172             this.show(this.triggerEl, false, false);
2173         }
2174         
2175         e.stopEvent();
2176     },
2177     
2178          
2179        
2180     
2181     hideMenuItems : function()
2182     {
2183         //$(backdrop).remove()
2184         Roo.select('.open',true).each(function(aa) {
2185             
2186             aa.removeClass('open');
2187           //var parent = getParent($(this))
2188           //var relatedTarget = { relatedTarget: this }
2189           
2190            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2191           //if (e.isDefaultPrevented()) return
2192            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2193         })
2194     },
2195     addxtypeChild : function (tree, cntr) {
2196         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2197           
2198         this.menuitems.add(comp);
2199         return comp;
2200
2201     },
2202     getEl : function()
2203     {
2204         Roo.log(this.el);
2205         return this.el;
2206     }
2207 });
2208
2209  
2210  /*
2211  * - LGPL
2212  *
2213  * menu item
2214  * 
2215  */
2216
2217
2218 /**
2219  * @class Roo.bootstrap.MenuItem
2220  * @extends Roo.bootstrap.Component
2221  * Bootstrap MenuItem class
2222  * @cfg {String} html the menu label
2223  * @cfg {String} href the link
2224  * @cfg {Boolean} preventDefault (true | false) default true
2225  * @cfg {Boolean} isContainer (true | false) default false
2226  * 
2227  * 
2228  * @constructor
2229  * Create a new MenuItem
2230  * @param {Object} config The config object
2231  */
2232
2233
2234 Roo.bootstrap.MenuItem = function(config){
2235     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2236     this.addEvents({
2237         // raw events
2238         /**
2239          * @event click
2240          * The raw click event for the entire grid.
2241          * @param {Roo.bootstrap.MenuItem} this
2242          * @param {Roo.EventObject} e
2243          */
2244         "click" : true
2245     });
2246 };
2247
2248 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2249     
2250     href : false,
2251     html : false,
2252     preventDefault: true,
2253     isContainer : false,
2254     
2255     getAutoCreate : function(){
2256         
2257         if(this.isContainer){
2258             return {
2259                 tag: 'li',
2260                 cls: 'dropdown-menu-item'
2261             };
2262         }
2263         
2264         var cfg= {
2265             tag: 'li',
2266             cls: 'dropdown-menu-item',
2267             cn: [
2268                     {
2269                         tag : 'a',
2270                         href : '#',
2271                         html : 'Link'
2272                     }
2273                 ]
2274         };
2275         if (this.parent().type == 'treeview') {
2276             cfg.cls = 'treeview-menu';
2277         }
2278         
2279         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2280         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2281         return cfg;
2282     },
2283     
2284     initEvents: function() {
2285         
2286         //this.el.select('a').on('click', this.onClick, this);
2287         
2288     },
2289     onClick : function(e)
2290     {
2291         Roo.log('item on click ');
2292         //if(this.preventDefault){
2293         //    e.preventDefault();
2294         //}
2295         //this.parent().hideMenuItems();
2296         
2297         this.fireEvent('click', this, e);
2298     },
2299     getEl : function()
2300     {
2301         return this.el;
2302     }
2303 });
2304
2305  
2306
2307  /*
2308  * - LGPL
2309  *
2310  * menu separator
2311  * 
2312  */
2313
2314
2315 /**
2316  * @class Roo.bootstrap.MenuSeparator
2317  * @extends Roo.bootstrap.Component
2318  * Bootstrap MenuSeparator class
2319  * 
2320  * @constructor
2321  * Create a new MenuItem
2322  * @param {Object} config The config object
2323  */
2324
2325
2326 Roo.bootstrap.MenuSeparator = function(config){
2327     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2328 };
2329
2330 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2331     
2332     getAutoCreate : function(){
2333         var cfg = {
2334             cls: 'divider',
2335             tag : 'li'
2336         };
2337         
2338         return cfg;
2339     }
2340    
2341 });
2342
2343  
2344
2345  
2346 /*
2347 * Licence: LGPL
2348 */
2349
2350 /**
2351  * @class Roo.bootstrap.Modal
2352  * @extends Roo.bootstrap.Component
2353  * Bootstrap Modal class
2354  * @cfg {String} title Title of dialog
2355  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2356  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2357  * @cfg {Boolean} specificTitle default false
2358  * @cfg {Array} buttons Array of buttons or standard button set..
2359  * @cfg {String} buttonPosition (left|right|center) default right
2360  * @cfg {Boolean} animate default true
2361  * @cfg {Boolean} allow_close default true
2362  * 
2363  * @constructor
2364  * Create a new Modal Dialog
2365  * @param {Object} config The config object
2366  */
2367
2368 Roo.bootstrap.Modal = function(config){
2369     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2370     this.addEvents({
2371         // raw events
2372         /**
2373          * @event btnclick
2374          * The raw btnclick event for the button
2375          * @param {Roo.EventObject} e
2376          */
2377         "btnclick" : true
2378     });
2379     this.buttons = this.buttons || [];
2380      
2381     if (this.tmpl) {
2382         this.tmpl = Roo.factory(this.tmpl);
2383     }
2384     
2385 };
2386
2387 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2388     
2389     title : 'test dialog',
2390    
2391     buttons : false,
2392     
2393     // set on load...
2394      
2395     html: false,
2396     
2397     tmp: false,
2398     
2399     specificTitle: false,
2400     
2401     buttonPosition: 'right',
2402     
2403     allow_close : true,
2404     
2405     animate : true,
2406     
2407     
2408      // private
2409     bodyEl:  false,
2410     footerEl:  false,
2411     titleEl:  false,
2412     closeEl:  false,
2413     
2414     
2415     onRender : function(ct, position)
2416     {
2417         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2418      
2419         if(!this.el){
2420             var cfg = Roo.apply({},  this.getAutoCreate());
2421             cfg.id = Roo.id();
2422             //if(!cfg.name){
2423             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2424             //}
2425             //if (!cfg.name.length) {
2426             //    delete cfg.name;
2427            // }
2428             if (this.cls) {
2429                 cfg.cls += ' ' + this.cls;
2430             }
2431             if (this.style) {
2432                 cfg.style = this.style;
2433             }
2434             this.el = Roo.get(document.body).createChild(cfg, position);
2435         }
2436         //var type = this.el.dom.type;
2437         
2438         
2439         if(this.tabIndex !== undefined){
2440             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2441         }
2442         
2443         
2444         this.bodyEl = this.el.select('.modal-body',true).first();
2445         this.closeEl = this.el.select('.modal-header .close', true).first();
2446         this.footerEl = this.el.select('.modal-footer',true).first();
2447         this.titleEl = this.el.select('.modal-title',true).first();
2448         
2449         
2450          
2451         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2452         this.maskEl.enableDisplayMode("block");
2453         this.maskEl.hide();
2454         //this.el.addClass("x-dlg-modal");
2455     
2456         if (this.buttons.length) {
2457             Roo.each(this.buttons, function(bb) {
2458                 var b = Roo.apply({}, bb);
2459                 b.xns = b.xns || Roo.bootstrap;
2460                 b.xtype = b.xtype || 'Button';
2461                 if (typeof(b.listeners) == 'undefined') {
2462                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2463                 }
2464                 
2465                 var btn = Roo.factory(b);
2466                 
2467                 btn.onRender(this.el.select('.modal-footer div').first());
2468                 
2469             },this);
2470         }
2471         // render the children.
2472         var nitems = [];
2473         
2474         if(typeof(this.items) != 'undefined'){
2475             var items = this.items;
2476             delete this.items;
2477
2478             for(var i =0;i < items.length;i++) {
2479                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2480             }
2481         }
2482         
2483         this.items = nitems;
2484         
2485         // where are these used - they used to be body/close/footer
2486         
2487        
2488         this.initEvents();
2489         //this.el.addClass([this.fieldClass, this.cls]);
2490         
2491     },
2492     
2493     getAutoCreate : function(){
2494         
2495         
2496         var bdy = {
2497                 cls : 'modal-body',
2498                 html : this.html || ''
2499         };
2500         
2501         var title = {
2502             tag: 'h4',
2503             cls : 'modal-title',
2504             html : this.title
2505         };
2506         
2507         if(this.specificTitle){
2508             title = this.title;
2509             
2510         };
2511         
2512         var header = [];
2513         if (this.allow_close) {
2514             header.push({
2515                 tag: 'button',
2516                 cls : 'close',
2517                 html : '&times'
2518             });
2519         }
2520         header.push(title);
2521         
2522         var modal = {
2523             cls: "modal",
2524             style : 'display: none',
2525             cn : [
2526                 {
2527                     cls: "modal-dialog",
2528                     cn : [
2529                         {
2530                             cls : "modal-content",
2531                             cn : [
2532                                 {
2533                                     cls : 'modal-header',
2534                                     cn : header
2535                                 },
2536                                 bdy,
2537                                 {
2538                                     cls : 'modal-footer',
2539                                     cn : [
2540                                         {
2541                                             tag: 'div',
2542                                             cls: 'btn-' + this.buttonPosition
2543                                         }
2544                                     ]
2545                                     
2546                                 }
2547                                 
2548                                 
2549                             ]
2550                             
2551                         }
2552                     ]
2553                         
2554                 }
2555             ]
2556         };
2557         
2558         if(this.animate){
2559             modal.cls += ' fade';
2560         }
2561         
2562         return modal;
2563           
2564     },
2565     getChildContainer : function() {
2566          
2567          return this.bodyEl;
2568         
2569     },
2570     getButtonContainer : function() {
2571          return this.el.select('.modal-footer div',true).first();
2572         
2573     },
2574     initEvents : function()
2575     {
2576         if (this.allow_close) {
2577             this.closeEl.on('click', this.hide, this);
2578         }
2579         
2580         var _this = this;
2581         
2582         window.addEventListener("resize", function() { _this.resize(); } );
2583
2584     },
2585     
2586     resize : function()
2587     {
2588         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2589     },
2590     
2591     show : function() {
2592         
2593         if (!this.rendered) {
2594             this.render();
2595         }
2596         
2597         this.el.setStyle('display', 'block');
2598         
2599         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2600             var _this = this;
2601             (function(){
2602                 this.el.addClass('in');
2603             }).defer(50, this);
2604         }else{
2605             this.el.addClass('in');
2606             
2607         }
2608         
2609         // not sure how we can show data in here.. 
2610         //if (this.tmpl) {
2611         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2612         //}
2613         
2614         Roo.get(document.body).addClass("x-body-masked");
2615         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2616         this.maskEl.show();
2617         this.el.setStyle('zIndex', '10001');
2618        
2619         this.fireEvent('show', this);
2620          
2621         
2622         
2623     },
2624     hide : function()
2625     {
2626         this.maskEl.hide();
2627         Roo.get(document.body).removeClass("x-body-masked");
2628         this.el.removeClass('in');
2629         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2630         
2631         if(this.animate){ // why
2632             var _this = this;
2633             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2634         }else{
2635             this.el.setStyle('display', 'none');
2636         }
2637         
2638         this.fireEvent('hide', this);
2639     },
2640     
2641     addButton : function(str, cb)
2642     {
2643          
2644         
2645         var b = Roo.apply({}, { html : str } );
2646         b.xns = b.xns || Roo.bootstrap;
2647         b.xtype = b.xtype || 'Button';
2648         if (typeof(b.listeners) == 'undefined') {
2649             b.listeners = { click : cb.createDelegate(this)  };
2650         }
2651         
2652         var btn = Roo.factory(b);
2653            
2654         btn.onRender(this.el.select('.modal-footer div').first());
2655         
2656         return btn;   
2657        
2658     },
2659     
2660     setDefaultButton : function(btn)
2661     {
2662         //this.el.select('.modal-footer').()
2663     },
2664     resizeTo: function(w,h)
2665     {
2666         // skip..
2667     },
2668     setContentSize  : function(w, h)
2669     {
2670         
2671     },
2672     onButtonClick: function(btn,e)
2673     {
2674         //Roo.log([a,b,c]);
2675         this.fireEvent('btnclick', btn.name, e);
2676     },
2677      /**
2678      * Set the title of the Dialog
2679      * @param {String} str new Title
2680      */
2681     setTitle: function(str) {
2682         this.titleEl.dom.innerHTML = str;    
2683     },
2684     /**
2685      * Set the body of the Dialog
2686      * @param {String} str new Title
2687      */
2688     setBody: function(str) {
2689         this.bodyEl.dom.innerHTML = str;    
2690     },
2691     /**
2692      * Set the body of the Dialog using the template
2693      * @param {Obj} data - apply this data to the template and replace the body contents.
2694      */
2695     applyBody: function(obj)
2696     {
2697         if (!this.tmpl) {
2698             Roo.log("Error - using apply Body without a template");
2699             //code
2700         }
2701         this.tmpl.overwrite(this.bodyEl, obj);
2702     }
2703     
2704 });
2705
2706
2707 Roo.apply(Roo.bootstrap.Modal,  {
2708     /**
2709          * Button config that displays a single OK button
2710          * @type Object
2711          */
2712         OK :  [{
2713             name : 'ok',
2714             weight : 'primary',
2715             html : 'OK'
2716         }], 
2717         /**
2718          * Button config that displays Yes and No buttons
2719          * @type Object
2720          */
2721         YESNO : [
2722             {
2723                 name  : 'no',
2724                 html : 'No'
2725             },
2726             {
2727                 name  :'yes',
2728                 weight : 'primary',
2729                 html : 'Yes'
2730             }
2731         ],
2732         
2733         /**
2734          * Button config that displays OK and Cancel buttons
2735          * @type Object
2736          */
2737         OKCANCEL : [
2738             {
2739                name : 'cancel',
2740                 html : 'Cancel'
2741             },
2742             {
2743                 name : 'ok',
2744                 weight : 'primary',
2745                 html : 'OK'
2746             }
2747         ],
2748         /**
2749          * Button config that displays Yes, No and Cancel buttons
2750          * @type Object
2751          */
2752         YESNOCANCEL : [
2753             {
2754                 name : 'yes',
2755                 weight : 'primary',
2756                 html : 'Yes'
2757             },
2758             {
2759                 name : 'no',
2760                 html : 'No'
2761             },
2762             {
2763                 name : 'cancel',
2764                 html : 'Cancel'
2765             }
2766         ]
2767 });
2768  
2769  /*
2770  * - LGPL
2771  *
2772  * messagebox - can be used as a replace
2773  * 
2774  */
2775 /**
2776  * @class Roo.MessageBox
2777  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2778  * Example usage:
2779  *<pre><code>
2780 // Basic alert:
2781 Roo.Msg.alert('Status', 'Changes saved successfully.');
2782
2783 // Prompt for user data:
2784 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2785     if (btn == 'ok'){
2786         // process text value...
2787     }
2788 });
2789
2790 // Show a dialog using config options:
2791 Roo.Msg.show({
2792    title:'Save Changes?',
2793    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2794    buttons: Roo.Msg.YESNOCANCEL,
2795    fn: processResult,
2796    animEl: 'elId'
2797 });
2798 </code></pre>
2799  * @singleton
2800  */
2801 Roo.bootstrap.MessageBox = function(){
2802     var dlg, opt, mask, waitTimer;
2803     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2804     var buttons, activeTextEl, bwidth;
2805
2806     
2807     // private
2808     var handleButton = function(button){
2809         dlg.hide();
2810         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2811     };
2812
2813     // private
2814     var handleHide = function(){
2815         if(opt && opt.cls){
2816             dlg.el.removeClass(opt.cls);
2817         }
2818         //if(waitTimer){
2819         //    Roo.TaskMgr.stop(waitTimer);
2820         //    waitTimer = null;
2821         //}
2822     };
2823
2824     // private
2825     var updateButtons = function(b){
2826         var width = 0;
2827         if(!b){
2828             buttons["ok"].hide();
2829             buttons["cancel"].hide();
2830             buttons["yes"].hide();
2831             buttons["no"].hide();
2832             //dlg.footer.dom.style.display = 'none';
2833             return width;
2834         }
2835         dlg.footerEl.dom.style.display = '';
2836         for(var k in buttons){
2837             if(typeof buttons[k] != "function"){
2838                 if(b[k]){
2839                     buttons[k].show();
2840                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2841                     width += buttons[k].el.getWidth()+15;
2842                 }else{
2843                     buttons[k].hide();
2844                 }
2845             }
2846         }
2847         return width;
2848     };
2849
2850     // private
2851     var handleEsc = function(d, k, e){
2852         if(opt && opt.closable !== false){
2853             dlg.hide();
2854         }
2855         if(e){
2856             e.stopEvent();
2857         }
2858     };
2859
2860     return {
2861         /**
2862          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2863          * @return {Roo.BasicDialog} The BasicDialog element
2864          */
2865         getDialog : function(){
2866            if(!dlg){
2867                 dlg = new Roo.bootstrap.Modal( {
2868                     //draggable: true,
2869                     //resizable:false,
2870                     //constraintoviewport:false,
2871                     //fixedcenter:true,
2872                     //collapsible : false,
2873                     //shim:true,
2874                     //modal: true,
2875                   //  width:400,
2876                   //  height:100,
2877                     //buttonAlign:"center",
2878                     closeClick : function(){
2879                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2880                             handleButton("no");
2881                         }else{
2882                             handleButton("cancel");
2883                         }
2884                     }
2885                 });
2886                 dlg.render();
2887                 dlg.on("hide", handleHide);
2888                 mask = dlg.mask;
2889                 //dlg.addKeyListener(27, handleEsc);
2890                 buttons = {};
2891                 this.buttons = buttons;
2892                 var bt = this.buttonText;
2893                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2894                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2895                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2896                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2897                 //Roo.log(buttons);
2898                 bodyEl = dlg.bodyEl.createChild({
2899
2900                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2901                         '<textarea class="roo-mb-textarea"></textarea>' +
2902                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2903                 });
2904                 msgEl = bodyEl.dom.firstChild;
2905                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2906                 textboxEl.enableDisplayMode();
2907                 textboxEl.addKeyListener([10,13], function(){
2908                     if(dlg.isVisible() && opt && opt.buttons){
2909                         if(opt.buttons.ok){
2910                             handleButton("ok");
2911                         }else if(opt.buttons.yes){
2912                             handleButton("yes");
2913                         }
2914                     }
2915                 });
2916                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2917                 textareaEl.enableDisplayMode();
2918                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2919                 progressEl.enableDisplayMode();
2920                 var pf = progressEl.dom.firstChild;
2921                 if (pf) {
2922                     pp = Roo.get(pf.firstChild);
2923                     pp.setHeight(pf.offsetHeight);
2924                 }
2925                 
2926             }
2927             return dlg;
2928         },
2929
2930         /**
2931          * Updates the message box body text
2932          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2933          * the XHTML-compliant non-breaking space character '&amp;#160;')
2934          * @return {Roo.MessageBox} This message box
2935          */
2936         updateText : function(text){
2937             if(!dlg.isVisible() && !opt.width){
2938                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2939             }
2940             msgEl.innerHTML = text || '&#160;';
2941       
2942             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2943             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2944             var w = Math.max(
2945                     Math.min(opt.width || cw , this.maxWidth), 
2946                     Math.max(opt.minWidth || this.minWidth, bwidth)
2947             );
2948             if(opt.prompt){
2949                 activeTextEl.setWidth(w);
2950             }
2951             if(dlg.isVisible()){
2952                 dlg.fixedcenter = false;
2953             }
2954             // to big, make it scroll. = But as usual stupid IE does not support
2955             // !important..
2956             
2957             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2958                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2959                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2960             } else {
2961                 bodyEl.dom.style.height = '';
2962                 bodyEl.dom.style.overflowY = '';
2963             }
2964             if (cw > w) {
2965                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2966             } else {
2967                 bodyEl.dom.style.overflowX = '';
2968             }
2969             
2970             dlg.setContentSize(w, bodyEl.getHeight());
2971             if(dlg.isVisible()){
2972                 dlg.fixedcenter = true;
2973             }
2974             return this;
2975         },
2976
2977         /**
2978          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2979          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2980          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2981          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2982          * @return {Roo.MessageBox} This message box
2983          */
2984         updateProgress : function(value, text){
2985             if(text){
2986                 this.updateText(text);
2987             }
2988             if (pp) { // weird bug on my firefox - for some reason this is not defined
2989                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2990             }
2991             return this;
2992         },        
2993
2994         /**
2995          * Returns true if the message box is currently displayed
2996          * @return {Boolean} True if the message box is visible, else false
2997          */
2998         isVisible : function(){
2999             return dlg && dlg.isVisible();  
3000         },
3001
3002         /**
3003          * Hides the message box if it is displayed
3004          */
3005         hide : function(){
3006             if(this.isVisible()){
3007                 dlg.hide();
3008             }  
3009         },
3010
3011         /**
3012          * Displays a new message box, or reinitializes an existing message box, based on the config options
3013          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3014          * The following config object properties are supported:
3015          * <pre>
3016 Property    Type             Description
3017 ----------  ---------------  ------------------------------------------------------------------------------------
3018 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3019                                    closes (defaults to undefined)
3020 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3021                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3022 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3023                                    progress and wait dialogs will ignore this property and always hide the
3024                                    close button as they can only be closed programmatically.
3025 cls               String           A custom CSS class to apply to the message box element
3026 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3027                                    displayed (defaults to 75)
3028 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3029                                    function will be btn (the name of the button that was clicked, if applicable,
3030                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3031                                    Progress and wait dialogs will ignore this option since they do not respond to
3032                                    user actions and can only be closed programmatically, so any required function
3033                                    should be called by the same code after it closes the dialog.
3034 icon              String           A CSS class that provides a background image to be used as an icon for
3035                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3036 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3037 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3038 modal             Boolean          False to allow user interaction with the page while the message box is
3039                                    displayed (defaults to true)
3040 msg               String           A string that will replace the existing message box body text (defaults
3041                                    to the XHTML-compliant non-breaking space character '&#160;')
3042 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3043 progress          Boolean          True to display a progress bar (defaults to false)
3044 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3045 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3046 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3047 title             String           The title text
3048 value             String           The string value to set into the active textbox element if displayed
3049 wait              Boolean          True to display a progress bar (defaults to false)
3050 width             Number           The width of the dialog in pixels
3051 </pre>
3052          *
3053          * Example usage:
3054          * <pre><code>
3055 Roo.Msg.show({
3056    title: 'Address',
3057    msg: 'Please enter your address:',
3058    width: 300,
3059    buttons: Roo.MessageBox.OKCANCEL,
3060    multiline: true,
3061    fn: saveAddress,
3062    animEl: 'addAddressBtn'
3063 });
3064 </code></pre>
3065          * @param {Object} config Configuration options
3066          * @return {Roo.MessageBox} This message box
3067          */
3068         show : function(options)
3069         {
3070             
3071             // this causes nightmares if you show one dialog after another
3072             // especially on callbacks..
3073              
3074             if(this.isVisible()){
3075                 
3076                 this.hide();
3077                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3078                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3079                 Roo.log("New Dialog Message:" +  options.msg )
3080                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3081                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3082                 
3083             }
3084             var d = this.getDialog();
3085             opt = options;
3086             d.setTitle(opt.title || "&#160;");
3087             d.closeEl.setDisplayed(opt.closable !== false);
3088             activeTextEl = textboxEl;
3089             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3090             if(opt.prompt){
3091                 if(opt.multiline){
3092                     textboxEl.hide();
3093                     textareaEl.show();
3094                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3095                         opt.multiline : this.defaultTextHeight);
3096                     activeTextEl = textareaEl;
3097                 }else{
3098                     textboxEl.show();
3099                     textareaEl.hide();
3100                 }
3101             }else{
3102                 textboxEl.hide();
3103                 textareaEl.hide();
3104             }
3105             progressEl.setDisplayed(opt.progress === true);
3106             this.updateProgress(0);
3107             activeTextEl.dom.value = opt.value || "";
3108             if(opt.prompt){
3109                 dlg.setDefaultButton(activeTextEl);
3110             }else{
3111                 var bs = opt.buttons;
3112                 var db = null;
3113                 if(bs && bs.ok){
3114                     db = buttons["ok"];
3115                 }else if(bs && bs.yes){
3116                     db = buttons["yes"];
3117                 }
3118                 dlg.setDefaultButton(db);
3119             }
3120             bwidth = updateButtons(opt.buttons);
3121             this.updateText(opt.msg);
3122             if(opt.cls){
3123                 d.el.addClass(opt.cls);
3124             }
3125             d.proxyDrag = opt.proxyDrag === true;
3126             d.modal = opt.modal !== false;
3127             d.mask = opt.modal !== false ? mask : false;
3128             if(!d.isVisible()){
3129                 // force it to the end of the z-index stack so it gets a cursor in FF
3130                 document.body.appendChild(dlg.el.dom);
3131                 d.animateTarget = null;
3132                 d.show(options.animEl);
3133             }
3134             return this;
3135         },
3136
3137         /**
3138          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3139          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3140          * and closing the message box when the process is complete.
3141          * @param {String} title The title bar text
3142          * @param {String} msg The message box body text
3143          * @return {Roo.MessageBox} This message box
3144          */
3145         progress : function(title, msg){
3146             this.show({
3147                 title : title,
3148                 msg : msg,
3149                 buttons: false,
3150                 progress:true,
3151                 closable:false,
3152                 minWidth: this.minProgressWidth,
3153                 modal : true
3154             });
3155             return this;
3156         },
3157
3158         /**
3159          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3160          * If a callback function is passed it will be called after the user clicks the button, and the
3161          * id of the button that was clicked will be passed as the only parameter to the callback
3162          * (could also be the top-right close button).
3163          * @param {String} title The title bar text
3164          * @param {String} msg The message box body text
3165          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3166          * @param {Object} scope (optional) The scope of the callback function
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         alert : function(title, msg, fn, scope){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: this.OK,
3174                 fn: fn,
3175                 scope : scope,
3176                 modal : true
3177             });
3178             return this;
3179         },
3180
3181         /**
3182          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3183          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3184          * You are responsible for closing the message box when the process is complete.
3185          * @param {String} msg The message box body text
3186          * @param {String} title (optional) The title bar text
3187          * @return {Roo.MessageBox} This message box
3188          */
3189         wait : function(msg, title){
3190             this.show({
3191                 title : title,
3192                 msg : msg,
3193                 buttons: false,
3194                 closable:false,
3195                 progress:true,
3196                 modal:true,
3197                 width:300,
3198                 wait:true
3199             });
3200             waitTimer = Roo.TaskMgr.start({
3201                 run: function(i){
3202                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3203                 },
3204                 interval: 1000
3205             });
3206             return this;
3207         },
3208
3209         /**
3210          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3211          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3212          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3213          * @param {String} title The title bar text
3214          * @param {String} msg The message box body text
3215          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3216          * @param {Object} scope (optional) The scope of the callback function
3217          * @return {Roo.MessageBox} This message box
3218          */
3219         confirm : function(title, msg, fn, scope){
3220             this.show({
3221                 title : title,
3222                 msg : msg,
3223                 buttons: this.YESNO,
3224                 fn: fn,
3225                 scope : scope,
3226                 modal : true
3227             });
3228             return this;
3229         },
3230
3231         /**
3232          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3233          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3234          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3235          * (could also be the top-right close button) and the text that was entered will be passed as the two
3236          * parameters to the callback.
3237          * @param {String} title The title bar text
3238          * @param {String} msg The message box body text
3239          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3240          * @param {Object} scope (optional) The scope of the callback function
3241          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3242          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3243          * @return {Roo.MessageBox} This message box
3244          */
3245         prompt : function(title, msg, fn, scope, multiline){
3246             this.show({
3247                 title : title,
3248                 msg : msg,
3249                 buttons: this.OKCANCEL,
3250                 fn: fn,
3251                 minWidth:250,
3252                 scope : scope,
3253                 prompt:true,
3254                 multiline: multiline,
3255                 modal : true
3256             });
3257             return this;
3258         },
3259
3260         /**
3261          * Button config that displays a single OK button
3262          * @type Object
3263          */
3264         OK : {ok:true},
3265         /**
3266          * Button config that displays Yes and No buttons
3267          * @type Object
3268          */
3269         YESNO : {yes:true, no:true},
3270         /**
3271          * Button config that displays OK and Cancel buttons
3272          * @type Object
3273          */
3274         OKCANCEL : {ok:true, cancel:true},
3275         /**
3276          * Button config that displays Yes, No and Cancel buttons
3277          * @type Object
3278          */
3279         YESNOCANCEL : {yes:true, no:true, cancel:true},
3280
3281         /**
3282          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3283          * @type Number
3284          */
3285         defaultTextHeight : 75,
3286         /**
3287          * The maximum width in pixels of the message box (defaults to 600)
3288          * @type Number
3289          */
3290         maxWidth : 600,
3291         /**
3292          * The minimum width in pixels of the message box (defaults to 100)
3293          * @type Number
3294          */
3295         minWidth : 100,
3296         /**
3297          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3298          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3299          * @type Number
3300          */
3301         minProgressWidth : 250,
3302         /**
3303          * An object containing the default button text strings that can be overriden for localized language support.
3304          * Supported properties are: ok, cancel, yes and no.
3305          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3306          * @type Object
3307          */
3308         buttonText : {
3309             ok : "OK",
3310             cancel : "Cancel",
3311             yes : "Yes",
3312             no : "No"
3313         }
3314     };
3315 }();
3316
3317 /**
3318  * Shorthand for {@link Roo.MessageBox}
3319  */
3320 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3321 Roo.Msg = Roo.Msg || Roo.MessageBox;
3322 /*
3323  * - LGPL
3324  *
3325  * navbar
3326  * 
3327  */
3328
3329 /**
3330  * @class Roo.bootstrap.Navbar
3331  * @extends Roo.bootstrap.Component
3332  * Bootstrap Navbar class
3333
3334  * @constructor
3335  * Create a new Navbar
3336  * @param {Object} config The config object
3337  */
3338
3339
3340 Roo.bootstrap.Navbar = function(config){
3341     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3342     
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3346     
3347     
3348    
3349     // private
3350     navItems : false,
3351     loadMask : false,
3352     
3353     
3354     getAutoCreate : function(){
3355         
3356         
3357         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3358         
3359     },
3360     
3361     initEvents :function ()
3362     {
3363         //Roo.log(this.el.select('.navbar-toggle',true));
3364         this.el.select('.navbar-toggle',true).on('click', function() {
3365            // Roo.log('click');
3366             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3367         }, this);
3368         
3369         var mark = {
3370             tag: "div",
3371             cls:"x-dlg-mask"
3372         };
3373         
3374         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3375         
3376         var size = this.el.getSize();
3377         this.maskEl.setSize(size.width, size.height);
3378         this.maskEl.enableDisplayMode("block");
3379         this.maskEl.hide();
3380         
3381         if(this.loadMask){
3382             this.maskEl.show();
3383         }
3384     },
3385     
3386     
3387     getChildContainer : function()
3388     {
3389         if (this.el.select('.collapse').getCount()) {
3390             return this.el.select('.collapse',true).first();
3391         }
3392         
3393         return this.el;
3394     },
3395     
3396     mask : function()
3397     {
3398         this.maskEl.show();
3399     },
3400     
3401     unmask : function()
3402     {
3403         this.maskEl.hide();
3404     } 
3405     
3406     
3407     
3408     
3409 });
3410
3411
3412
3413  
3414
3415  /*
3416  * - LGPL
3417  *
3418  * navbar
3419  * 
3420  */
3421
3422 /**
3423  * @class Roo.bootstrap.NavSimplebar
3424  * @extends Roo.bootstrap.Navbar
3425  * Bootstrap Sidebar class
3426  *
3427  * @cfg {Boolean} inverse is inverted color
3428  * 
3429  * @cfg {String} type (nav | pills | tabs)
3430  * @cfg {Boolean} arrangement stacked | justified
3431  * @cfg {String} align (left | right) alignment
3432  * 
3433  * @cfg {Boolean} main (true|false) main nav bar? default false
3434  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3435  * 
3436  * @cfg {String} tag (header|footer|nav|div) default is nav 
3437
3438  * 
3439  * 
3440  * 
3441  * @constructor
3442  * Create a new Sidebar
3443  * @param {Object} config The config object
3444  */
3445
3446
3447 Roo.bootstrap.NavSimplebar = function(config){
3448     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3449 };
3450
3451 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3452     
3453     inverse: false,
3454     
3455     type: false,
3456     arrangement: '',
3457     align : false,
3458     
3459     
3460     
3461     main : false,
3462     
3463     
3464     tag : false,
3465     
3466     
3467     getAutoCreate : function(){
3468         
3469         
3470         var cfg = {
3471             tag : this.tag || 'div',
3472             cls : 'navbar'
3473         };
3474           
3475         
3476         cfg.cn = [
3477             {
3478                 cls: 'nav',
3479                 tag : 'ul'
3480             }
3481         ];
3482         
3483          
3484         this.type = this.type || 'nav';
3485         if (['tabs','pills'].indexOf(this.type)!==-1) {
3486             cfg.cn[0].cls += ' nav-' + this.type
3487         
3488         
3489         } else {
3490             if (this.type!=='nav') {
3491                 Roo.log('nav type must be nav/tabs/pills')
3492             }
3493             cfg.cn[0].cls += ' navbar-nav'
3494         }
3495         
3496         
3497         
3498         
3499         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3500             cfg.cn[0].cls += ' nav-' + this.arrangement;
3501         }
3502         
3503         
3504         if (this.align === 'right') {
3505             cfg.cn[0].cls += ' navbar-right';
3506         }
3507         
3508         if (this.inverse) {
3509             cfg.cls += ' navbar-inverse';
3510             
3511         }
3512         
3513         
3514         return cfg;
3515     
3516         
3517     }
3518     
3519     
3520     
3521 });
3522
3523
3524
3525  
3526
3527  
3528        /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.NavHeaderbar
3537  * @extends Roo.bootstrap.NavSimplebar
3538  * Bootstrap Sidebar class
3539  *
3540  * @cfg {String} brand what is brand
3541  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3542  * @cfg {String} brand_href href of the brand
3543  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3544  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3545  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3546  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3547  * 
3548  * @constructor
3549  * Create a new Sidebar
3550  * @param {Object} config The config object
3551  */
3552
3553
3554 Roo.bootstrap.NavHeaderbar = function(config){
3555     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3556       
3557 };
3558
3559 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3560     
3561     position: '',
3562     brand: '',
3563     brand_href: false,
3564     srButton : true,
3565     autohide : false,
3566     desktopCenter : false,
3567    
3568     
3569     getAutoCreate : function(){
3570         
3571         var   cfg = {
3572             tag: this.nav || 'nav',
3573             cls: 'navbar',
3574             role: 'navigation',
3575             cn: []
3576         };
3577         
3578         var cn = cfg.cn;
3579         if (this.desktopCenter) {
3580             cn.push({cls : 'container', cn : []});
3581             cn = cn[0].cn;
3582         }
3583         
3584         if(this.srButton){
3585             cn.push({
3586                 tag: 'div',
3587                 cls: 'navbar-header',
3588                 cn: [
3589                     {
3590                         tag: 'button',
3591                         type: 'button',
3592                         cls: 'navbar-toggle',
3593                         'data-toggle': 'collapse',
3594                         cn: [
3595                             {
3596                                 tag: 'span',
3597                                 cls: 'sr-only',
3598                                 html: 'Toggle navigation'
3599                             },
3600                             {
3601                                 tag: 'span',
3602                                 cls: 'icon-bar'
3603                             },
3604                             {
3605                                 tag: 'span',
3606                                 cls: 'icon-bar'
3607                             },
3608                             {
3609                                 tag: 'span',
3610                                 cls: 'icon-bar'
3611                             }
3612                         ]
3613                     }
3614                 ]
3615             });
3616         }
3617         
3618         cn.push({
3619             tag: 'div',
3620             cls: 'collapse navbar-collapse',
3621             cn : []
3622         });
3623         
3624         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3625         
3626         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3627             cfg.cls += ' navbar-' + this.position;
3628             
3629             // tag can override this..
3630             
3631             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3632         }
3633         
3634         if (this.brand !== '') {
3635             cn[0].cn.push({
3636                 tag: 'a',
3637                 href: this.brand_href ? this.brand_href : '#',
3638                 cls: 'navbar-brand',
3639                 cn: [
3640                 this.brand
3641                 ]
3642             });
3643         }
3644         
3645         if(this.main){
3646             cfg.cls += ' main-nav';
3647         }
3648         
3649         
3650         return cfg;
3651
3652         
3653     },
3654     getHeaderChildContainer : function()
3655     {
3656         if (this.el.select('.navbar-header').getCount()) {
3657             return this.el.select('.navbar-header',true).first();
3658         }
3659         
3660         return this.getChildContainer();
3661     },
3662     
3663     
3664     initEvents : function()
3665     {
3666         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3667         
3668         if (this.autohide) {
3669             
3670             var prevScroll = 0;
3671             var ft = this.el;
3672             
3673             Roo.get(document).on('scroll',function(e) {
3674                 var ns = Roo.get(document).getScroll().top;
3675                 var os = prevScroll;
3676                 prevScroll = ns;
3677                 
3678                 if(ns > os){
3679                     ft.removeClass('slideDown');
3680                     ft.addClass('slideUp');
3681                     return;
3682                 }
3683                 ft.removeClass('slideUp');
3684                 ft.addClass('slideDown');
3685                  
3686               
3687           },this);
3688         }
3689     }    
3690     
3691 });
3692
3693
3694
3695  
3696
3697  /*
3698  * - LGPL
3699  *
3700  * navbar
3701  * 
3702  */
3703
3704 /**
3705  * @class Roo.bootstrap.NavSidebar
3706  * @extends Roo.bootstrap.Navbar
3707  * Bootstrap Sidebar class
3708  * 
3709  * @constructor
3710  * Create a new Sidebar
3711  * @param {Object} config The config object
3712  */
3713
3714
3715 Roo.bootstrap.NavSidebar = function(config){
3716     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3717 };
3718
3719 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3720     
3721     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3722     
3723     getAutoCreate : function(){
3724         
3725         
3726         return  {
3727             tag: 'div',
3728             cls: 'sidebar sidebar-nav'
3729         };
3730     
3731         
3732     }
3733     
3734     
3735     
3736 });
3737
3738
3739
3740  
3741
3742  /*
3743  * - LGPL
3744  *
3745  * nav group
3746  * 
3747  */
3748
3749 /**
3750  * @class Roo.bootstrap.NavGroup
3751  * @extends Roo.bootstrap.Component
3752  * Bootstrap NavGroup class
3753  * @cfg {String} align (left|right)
3754  * @cfg {Boolean} inverse
3755  * @cfg {String} type (nav|pills|tab) default nav
3756  * @cfg {String} navId - reference Id for navbar.
3757
3758  * 
3759  * @constructor
3760  * Create a new nav group
3761  * @param {Object} config The config object
3762  */
3763
3764 Roo.bootstrap.NavGroup = function(config){
3765     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3766     this.navItems = [];
3767    
3768     Roo.bootstrap.NavGroup.register(this);
3769      this.addEvents({
3770         /**
3771              * @event changed
3772              * Fires when the active item changes
3773              * @param {Roo.bootstrap.NavGroup} this
3774              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3775              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3776          */
3777         'changed': true
3778      });
3779     
3780 };
3781
3782 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3783     
3784     align: '',
3785     inverse: false,
3786     form: false,
3787     type: 'nav',
3788     navId : '',
3789     // private
3790     
3791     navItems : false, 
3792     
3793     getAutoCreate : function()
3794     {
3795         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3796         
3797         cfg = {
3798             tag : 'ul',
3799             cls: 'nav' 
3800         };
3801         
3802         if (['tabs','pills'].indexOf(this.type)!==-1) {
3803             cfg.cls += ' nav-' + this.type
3804         } else {
3805             if (this.type!=='nav') {
3806                 Roo.log('nav type must be nav/tabs/pills')
3807             }
3808             cfg.cls += ' navbar-nav'
3809         }
3810         
3811         if (this.parent().sidebar) {
3812             cfg = {
3813                 tag: 'ul',
3814                 cls: 'dashboard-menu sidebar-menu'
3815             };
3816             
3817             return cfg;
3818         }
3819         
3820         if (this.form === true) {
3821             cfg = {
3822                 tag: 'form',
3823                 cls: 'navbar-form'
3824             };
3825             
3826             if (this.align === 'right') {
3827                 cfg.cls += ' navbar-right';
3828             } else {
3829                 cfg.cls += ' navbar-left';
3830             }
3831         }
3832         
3833         if (this.align === 'right') {
3834             cfg.cls += ' navbar-right';
3835         }
3836         
3837         if (this.inverse) {
3838             cfg.cls += ' navbar-inverse';
3839             
3840         }
3841         
3842         
3843         return cfg;
3844     },
3845     /**
3846     * sets the active Navigation item
3847     * @param {Roo.bootstrap.NavItem} the new current navitem
3848     */
3849     setActiveItem : function(item)
3850     {
3851         var prev = false;
3852         Roo.each(this.navItems, function(v){
3853             if (v == item) {
3854                 return ;
3855             }
3856             if (v.isActive()) {
3857                 v.setActive(false, true);
3858                 prev = v;
3859                 
3860             }
3861             
3862         });
3863
3864         item.setActive(true, true);
3865         this.fireEvent('changed', this, item, prev);
3866         
3867         
3868     },
3869     /**
3870     * gets the active Navigation item
3871     * @return {Roo.bootstrap.NavItem} the current navitem
3872     */
3873     getActive : function()
3874     {
3875         
3876         var prev = false;
3877         Roo.each(this.navItems, function(v){
3878             
3879             if (v.isActive()) {
3880                 prev = v;
3881                 
3882             }
3883             
3884         });
3885         return prev;
3886     },
3887     
3888     indexOfNav : function()
3889     {
3890         
3891         var prev = false;
3892         Roo.each(this.navItems, function(v,i){
3893             
3894             if (v.isActive()) {
3895                 prev = i;
3896                 
3897             }
3898             
3899         });
3900         return prev;
3901     },
3902     /**
3903     * adds a Navigation item
3904     * @param {Roo.bootstrap.NavItem} the navitem to add
3905     */
3906     addItem : function(cfg)
3907     {
3908         var cn = new Roo.bootstrap.NavItem(cfg);
3909         this.register(cn);
3910         cn.parentId = this.id;
3911         cn.onRender(this.el, null);
3912         return cn;
3913     },
3914     /**
3915     * register a Navigation item
3916     * @param {Roo.bootstrap.NavItem} the navitem to add
3917     */
3918     register : function(item)
3919     {
3920         this.navItems.push( item);
3921         item.navId = this.navId;
3922     
3923     },
3924     
3925     /**
3926     * clear all the Navigation item
3927     */
3928    
3929     clearAll : function()
3930     {
3931         this.navItems = [];
3932         this.el.dom.innerHTML = '';
3933     },
3934     
3935     getNavItem: function(tabId)
3936     {
3937         var ret = false;
3938         Roo.each(this.navItems, function(e) {
3939             if (e.tabId == tabId) {
3940                ret =  e;
3941                return false;
3942             }
3943             return true;
3944             
3945         });
3946         return ret;
3947     },
3948     
3949     setActiveNext : function()
3950     {
3951         var i = this.indexOfNav(this.getActive());
3952         if (i > this.navItems.length) {
3953             return;
3954         }
3955         this.setActiveItem(this.navItems[i+1]);
3956     },
3957     setActivePrev : function()
3958     {
3959         var i = this.indexOfNav(this.getActive());
3960         if (i  < 1) {
3961             return;
3962         }
3963         this.setActiveItem(this.navItems[i-1]);
3964     },
3965     clearWasActive : function(except) {
3966         Roo.each(this.navItems, function(e) {
3967             if (e.tabId != except.tabId && e.was_active) {
3968                e.was_active = false;
3969                return false;
3970             }
3971             return true;
3972             
3973         });
3974     },
3975     getWasActive : function ()
3976     {
3977         var r = false;
3978         Roo.each(this.navItems, function(e) {
3979             if (e.was_active) {
3980                r = e;
3981                return false;
3982             }
3983             return true;
3984             
3985         });
3986         return r;
3987     }
3988     
3989     
3990 });
3991
3992  
3993 Roo.apply(Roo.bootstrap.NavGroup, {
3994     
3995     groups: {},
3996      /**
3997     * register a Navigation Group
3998     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3999     */
4000     register : function(navgrp)
4001     {
4002         this.groups[navgrp.navId] = navgrp;
4003         
4004     },
4005     /**
4006     * fetch a Navigation Group based on the navigation ID
4007     * @param {string} the navgroup to add
4008     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4009     */
4010     get: function(navId) {
4011         if (typeof(this.groups[navId]) == 'undefined') {
4012             return false;
4013             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4014         }
4015         return this.groups[navId] ;
4016     }
4017     
4018     
4019     
4020 });
4021
4022  /*
4023  * - LGPL
4024  *
4025  * row
4026  * 
4027  */
4028
4029 /**
4030  * @class Roo.bootstrap.NavItem
4031  * @extends Roo.bootstrap.Component
4032  * Bootstrap Navbar.NavItem class
4033  * @cfg {String} href  link to
4034  * @cfg {String} html content of button
4035  * @cfg {String} badge text inside badge
4036  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4037  * @cfg {String} glyphicon name of glyphicon
4038  * @cfg {String} icon name of font awesome icon
4039  * @cfg {Boolean} active Is item active
4040  * @cfg {Boolean} disabled Is item disabled
4041  
4042  * @cfg {Boolean} preventDefault (true | false) default false
4043  * @cfg {String} tabId the tab that this item activates.
4044  * @cfg {String} tagtype (a|span) render as a href or span?
4045  * @cfg {Boolean} animateRef (true|false) link to element default false  
4046   
4047  * @constructor
4048  * Create a new Navbar Item
4049  * @param {Object} config The config object
4050  */
4051 Roo.bootstrap.NavItem = function(config){
4052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4053     this.addEvents({
4054         // raw events
4055         /**
4056          * @event click
4057          * The raw click event for the entire grid.
4058          * @param {Roo.EventObject} e
4059          */
4060         "click" : true,
4061          /**
4062             * @event changed
4063             * Fires when the active item active state changes
4064             * @param {Roo.bootstrap.NavItem} this
4065             * @param {boolean} state the new state
4066              
4067          */
4068         'changed': true,
4069         /**
4070             * @event scrollto
4071             * Fires when scroll to element
4072             * @param {Roo.bootstrap.NavItem} this
4073             * @param {Object} options
4074             * @param {Roo.EventObject} e
4075              
4076          */
4077         'scrollto': true
4078     });
4079    
4080 };
4081
4082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4083     
4084     href: false,
4085     html: '',
4086     badge: '',
4087     icon: false,
4088     glyphicon: false,
4089     active: false,
4090     preventDefault : false,
4091     tabId : false,
4092     tagtype : 'a',
4093     disabled : false,
4094     animateRef : false,
4095     was_active : false,
4096     
4097     getAutoCreate : function(){
4098          
4099         var cfg = {
4100             tag: 'li',
4101             cls: 'nav-item'
4102             
4103         };
4104         
4105         if (this.active) {
4106             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4107         }
4108         if (this.disabled) {
4109             cfg.cls += ' disabled';
4110         }
4111         
4112         if (this.href || this.html || this.glyphicon || this.icon) {
4113             cfg.cn = [
4114                 {
4115                     tag: this.tagtype,
4116                     href : this.href || "#",
4117                     html: this.html || ''
4118                 }
4119             ];
4120             
4121             if (this.icon) {
4122                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4123             }
4124
4125             if(this.glyphicon) {
4126                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4127             }
4128             
4129             if (this.menu) {
4130                 
4131                 cfg.cn[0].html += " <span class='caret'></span>";
4132              
4133             }
4134             
4135             if (this.badge !== '') {
4136                  
4137                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4138             }
4139         }
4140         
4141         
4142         
4143         return cfg;
4144     },
4145     initEvents: function() 
4146     {
4147         if (typeof (this.menu) != 'undefined') {
4148             this.menu.parentType = this.xtype;
4149             this.menu.triggerEl = this.el;
4150             this.menu = this.addxtype(Roo.apply({}, this.menu));
4151         }
4152         
4153         this.el.select('a',true).on('click', this.onClick, this);
4154         
4155         if(this.tagtype == 'span'){
4156             this.el.select('span',true).on('click', this.onClick, this);
4157         }
4158        
4159         // at this point parent should be available..
4160         this.parent().register(this);
4161     },
4162     
4163     onClick : function(e)
4164     {
4165         if(
4166                 this.preventDefault || 
4167                 this.href == '#' 
4168         ){
4169             
4170             e.preventDefault();
4171         }
4172         
4173         if (this.disabled) {
4174             return;
4175         }
4176         
4177         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4178         if (tg && tg.transition) {
4179             Roo.log("waiting for the transitionend");
4180             return;
4181         }
4182         
4183         
4184         
4185         //Roo.log("fire event clicked");
4186         if(this.fireEvent('click', this, e) === false){
4187             return;
4188         };
4189         
4190         if(this.tagtype == 'span'){
4191             return;
4192         }
4193         
4194         //Roo.log(this.href);
4195         var ael = this.el.select('a',true).first();
4196         //Roo.log(ael);
4197         
4198         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4199             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4200             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4201                 return; // ignore... - it's a 'hash' to another page.
4202             }
4203             
4204             e.preventDefault();
4205             this.scrollToElement(e);
4206         }
4207         
4208         
4209         var p =  this.parent();
4210    
4211         if (['tabs','pills'].indexOf(p.type)!==-1) {
4212             if (typeof(p.setActiveItem) !== 'undefined') {
4213                 p.setActiveItem(this);
4214             }
4215         }
4216         
4217         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4218         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4219             // remove the collapsed menu expand...
4220             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4221         }
4222     },
4223     
4224     isActive: function () {
4225         return this.active
4226     },
4227     setActive : function(state, fire, is_was_active)
4228     {
4229         if (this.active && !state && this.navId) {
4230             this.was_active = true;
4231             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4232             if (nv) {
4233                 nv.clearWasActive(this);
4234             }
4235             
4236         }
4237         this.active = state;
4238         
4239         if (!state ) {
4240             this.el.removeClass('active');
4241         } else if (!this.el.hasClass('active')) {
4242             this.el.addClass('active');
4243         }
4244         if (fire) {
4245             this.fireEvent('changed', this, state);
4246         }
4247         
4248         // show a panel if it's registered and related..
4249         
4250         if (!this.navId || !this.tabId || !state || is_was_active) {
4251             return;
4252         }
4253         
4254         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4255         if (!tg) {
4256             return;
4257         }
4258         var pan = tg.getPanelByName(this.tabId);
4259         if (!pan) {
4260             return;
4261         }
4262         // if we can not flip to new panel - go back to old nav highlight..
4263         if (false == tg.showPanel(pan)) {
4264             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4265             if (nv) {
4266                 var onav = nv.getWasActive();
4267                 if (onav) {
4268                     onav.setActive(true, false, true);
4269                 }
4270             }
4271             
4272         }
4273         
4274         
4275         
4276     },
4277      // this should not be here...
4278     setDisabled : function(state)
4279     {
4280         this.disabled = state;
4281         if (!state ) {
4282             this.el.removeClass('disabled');
4283         } else if (!this.el.hasClass('disabled')) {
4284             this.el.addClass('disabled');
4285         }
4286         
4287     },
4288     
4289     /**
4290      * Fetch the element to display the tooltip on.
4291      * @return {Roo.Element} defaults to this.el
4292      */
4293     tooltipEl : function()
4294     {
4295         return this.el.select('' + this.tagtype + '', true).first();
4296     },
4297     
4298     scrollToElement : function(e)
4299     {
4300         var c = document.body;
4301         
4302         /*
4303          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4304          */
4305         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4306             c = document.documentElement;
4307         }
4308         
4309         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4310         
4311         if(!target){
4312             return;
4313         }
4314
4315         var o = target.calcOffsetsTo(c);
4316         
4317         var options = {
4318             target : target,
4319             value : o[1]
4320         };
4321         
4322         this.fireEvent('scrollto', this, options, e);
4323         
4324         Roo.get(c).scrollTo('top', options.value, true);
4325         
4326         return;
4327     }
4328 });
4329  
4330
4331  /*
4332  * - LGPL
4333  *
4334  * sidebar item
4335  *
4336  *  li
4337  *    <span> icon </span>
4338  *    <span> text </span>
4339  *    <span>badge </span>
4340  */
4341
4342 /**
4343  * @class Roo.bootstrap.NavSidebarItem
4344  * @extends Roo.bootstrap.NavItem
4345  * Bootstrap Navbar.NavSidebarItem class
4346  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4347  * @constructor
4348  * Create a new Navbar Button
4349  * @param {Object} config The config object
4350  */
4351 Roo.bootstrap.NavSidebarItem = function(config){
4352     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4353     this.addEvents({
4354         // raw events
4355         /**
4356          * @event click
4357          * The raw click event for the entire grid.
4358          * @param {Roo.EventObject} e
4359          */
4360         "click" : true,
4361          /**
4362             * @event changed
4363             * Fires when the active item active state changes
4364             * @param {Roo.bootstrap.NavSidebarItem} this
4365             * @param {boolean} state the new state
4366              
4367          */
4368         'changed': true
4369     });
4370    
4371 };
4372
4373 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4374     
4375     badgeWeight : 'default',
4376     
4377     getAutoCreate : function(){
4378         
4379         
4380         var a = {
4381                 tag: 'a',
4382                 href : this.href || '#',
4383                 cls: '',
4384                 html : '',
4385                 cn : []
4386         };
4387         var cfg = {
4388             tag: 'li',
4389             cls: '',
4390             cn: [ a ]
4391         };
4392         var span = {
4393             tag: 'span',
4394             html : this.html || ''
4395         };
4396         
4397         
4398         if (this.active) {
4399             cfg.cls += ' active';
4400         }
4401         
4402         if (this.disabled) {
4403             cfg.cls += ' disabled';
4404         }
4405         
4406         // left icon..
4407         if (this.glyphicon || this.icon) {
4408             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4409             a.cn.push({ tag : 'i', cls : c }) ;
4410         }
4411         // html..
4412         a.cn.push(span);
4413         // then badge..
4414         if (this.badge !== '') {
4415             
4416             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4417         }
4418         // fi
4419         if (this.menu) {
4420             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4421             a.cls += 'dropdown-toggle treeview' ;
4422             
4423         }
4424         
4425         
4426         
4427         return cfg;
4428          
4429            
4430     },
4431     
4432     initEvents : function()
4433     { 
4434         this.el.on('click', this.onClick, this);
4435        
4436     
4437         if(this.badge !== ''){
4438  
4439             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4440         }
4441         
4442     },
4443     
4444     onClick : function(e)
4445     {
4446         if(this.disabled){
4447             e.preventDefault();
4448             return;
4449         }
4450         
4451         if(this.preventDefault){
4452             e.preventDefault();
4453         }
4454         
4455         this.fireEvent('click', this);
4456     },
4457     
4458     disable : function()
4459     {
4460         this.setDisabled(true);
4461     },
4462     
4463     enable : function()
4464     {
4465         this.setDisabled(false);
4466     },
4467     
4468     setDisabled : function(state)
4469     {
4470         if(this.disabled == state){
4471             return;
4472         }
4473         
4474         this.disabled = state;
4475         
4476         if (state) {
4477             this.el.addClass('disabled');
4478             return;
4479         }
4480         
4481         this.el.removeClass('disabled');
4482         
4483         return;
4484     },
4485     
4486     setActive : function(state)
4487     {
4488         if(this.active == state){
4489             return;
4490         }
4491         
4492         this.active = state;
4493         
4494         if (state) {
4495             this.el.addClass('active');
4496             return;
4497         }
4498         
4499         this.el.removeClass('active');
4500         
4501         return;
4502     },
4503     
4504     isActive: function () 
4505     {
4506         return this.active;
4507     },
4508     
4509     setBadge : function(str)
4510     {
4511         if(!this.badgeEl){
4512             return;
4513         }
4514         
4515         this.badgeEl.dom.innerHTML = str;
4516     }
4517     
4518    
4519      
4520  
4521 });
4522  
4523
4524  /*
4525  * - LGPL
4526  *
4527  * row
4528  * 
4529  */
4530
4531 /**
4532  * @class Roo.bootstrap.Row
4533  * @extends Roo.bootstrap.Component
4534  * Bootstrap Row class (contains columns...)
4535  * 
4536  * @constructor
4537  * Create a new Row
4538  * @param {Object} config The config object
4539  */
4540
4541 Roo.bootstrap.Row = function(config){
4542     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4543 };
4544
4545 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4546     
4547     getAutoCreate : function(){
4548        return {
4549             cls: 'row clearfix'
4550        };
4551     }
4552     
4553     
4554 });
4555
4556  
4557
4558  /*
4559  * - LGPL
4560  *
4561  * element
4562  * 
4563  */
4564
4565 /**
4566  * @class Roo.bootstrap.Element
4567  * @extends Roo.bootstrap.Component
4568  * Bootstrap Element class
4569  * @cfg {String} html contents of the element
4570  * @cfg {String} tag tag of the element
4571  * @cfg {String} cls class of the element
4572  * @cfg {Boolean} preventDefault (true|false) default false
4573  * @cfg {Boolean} clickable (true|false) default false
4574  * 
4575  * @constructor
4576  * Create a new Element
4577  * @param {Object} config The config object
4578  */
4579
4580 Roo.bootstrap.Element = function(config){
4581     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4582     
4583     this.addEvents({
4584         // raw events
4585         /**
4586          * @event click
4587          * When a element is chick
4588          * @param {Roo.bootstrap.Element} this
4589          * @param {Roo.EventObject} e
4590          */
4591         "click" : true
4592     });
4593 };
4594
4595 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4596     
4597     tag: 'div',
4598     cls: '',
4599     html: '',
4600     preventDefault: false, 
4601     clickable: false,
4602     
4603     getAutoCreate : function(){
4604         
4605         var cfg = {
4606             tag: this.tag,
4607             cls: this.cls,
4608             html: this.html
4609         };
4610         
4611         return cfg;
4612     },
4613     
4614     initEvents: function() 
4615     {
4616         Roo.bootstrap.Element.superclass.initEvents.call(this);
4617         
4618         if(this.clickable){
4619             this.el.on('click', this.onClick, this);
4620         }
4621         
4622     },
4623     
4624     onClick : function(e)
4625     {
4626         if(this.preventDefault){
4627             e.preventDefault();
4628         }
4629         
4630         this.fireEvent('click', this, e);
4631     },
4632     
4633     getValue : function()
4634     {
4635         return this.el.dom.innerHTML;
4636     },
4637     
4638     setValue : function(value)
4639     {
4640         this.el.dom.innerHTML = value;
4641     }
4642    
4643 });
4644
4645  
4646
4647  /*
4648  * - LGPL
4649  *
4650  * pagination
4651  * 
4652  */
4653
4654 /**
4655  * @class Roo.bootstrap.Pagination
4656  * @extends Roo.bootstrap.Component
4657  * Bootstrap Pagination class
4658  * @cfg {String} size xs | sm | md | lg
4659  * @cfg {Boolean} inverse false | true
4660  * 
4661  * @constructor
4662  * Create a new Pagination
4663  * @param {Object} config The config object
4664  */
4665
4666 Roo.bootstrap.Pagination = function(config){
4667     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4668 };
4669
4670 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4671     
4672     cls: false,
4673     size: false,
4674     inverse: false,
4675     
4676     getAutoCreate : function(){
4677         var cfg = {
4678             tag: 'ul',
4679                 cls: 'pagination'
4680         };
4681         if (this.inverse) {
4682             cfg.cls += ' inverse';
4683         }
4684         if (this.html) {
4685             cfg.html=this.html;
4686         }
4687         if (this.cls) {
4688             cfg.cls += " " + this.cls;
4689         }
4690         return cfg;
4691     }
4692    
4693 });
4694
4695  
4696
4697  /*
4698  * - LGPL
4699  *
4700  * Pagination item
4701  * 
4702  */
4703
4704
4705 /**
4706  * @class Roo.bootstrap.PaginationItem
4707  * @extends Roo.bootstrap.Component
4708  * Bootstrap PaginationItem class
4709  * @cfg {String} html text
4710  * @cfg {String} href the link
4711  * @cfg {Boolean} preventDefault (true | false) default true
4712  * @cfg {Boolean} active (true | false) default false
4713  * @cfg {Boolean} disabled default false
4714  * 
4715  * 
4716  * @constructor
4717  * Create a new PaginationItem
4718  * @param {Object} config The config object
4719  */
4720
4721
4722 Roo.bootstrap.PaginationItem = function(config){
4723     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4724     this.addEvents({
4725         // raw events
4726         /**
4727          * @event click
4728          * The raw click event for the entire grid.
4729          * @param {Roo.EventObject} e
4730          */
4731         "click" : true
4732     });
4733 };
4734
4735 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4736     
4737     href : false,
4738     html : false,
4739     preventDefault: true,
4740     active : false,
4741     cls : false,
4742     disabled: false,
4743     
4744     getAutoCreate : function(){
4745         var cfg= {
4746             tag: 'li',
4747             cn: [
4748                 {
4749                     tag : 'a',
4750                     href : this.href ? this.href : '#',
4751                     html : this.html ? this.html : ''
4752                 }
4753             ]
4754         };
4755         
4756         if(this.cls){
4757             cfg.cls = this.cls;
4758         }
4759         
4760         if(this.disabled){
4761             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4762         }
4763         
4764         if(this.active){
4765             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4766         }
4767         
4768         return cfg;
4769     },
4770     
4771     initEvents: function() {
4772         
4773         this.el.on('click', this.onClick, this);
4774         
4775     },
4776     onClick : function(e)
4777     {
4778         Roo.log('PaginationItem on click ');
4779         if(this.preventDefault){
4780             e.preventDefault();
4781         }
4782         
4783         if(this.disabled){
4784             return;
4785         }
4786         
4787         this.fireEvent('click', this, e);
4788     }
4789    
4790 });
4791
4792  
4793
4794  /*
4795  * - LGPL
4796  *
4797  * slider
4798  * 
4799  */
4800
4801
4802 /**
4803  * @class Roo.bootstrap.Slider
4804  * @extends Roo.bootstrap.Component
4805  * Bootstrap Slider class
4806  *    
4807  * @constructor
4808  * Create a new Slider
4809  * @param {Object} config The config object
4810  */
4811
4812 Roo.bootstrap.Slider = function(config){
4813     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4814 };
4815
4816 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4817     
4818     getAutoCreate : function(){
4819         
4820         var cfg = {
4821             tag: 'div',
4822             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4823             cn: [
4824                 {
4825                     tag: 'a',
4826                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4827                 }
4828             ]
4829         };
4830         
4831         return cfg;
4832     }
4833    
4834 });
4835
4836  /*
4837  * Based on:
4838  * Ext JS Library 1.1.1
4839  * Copyright(c) 2006-2007, Ext JS, LLC.
4840  *
4841  * Originally Released Under LGPL - original licence link has changed is not relivant.
4842  *
4843  * Fork - LGPL
4844  * <script type="text/javascript">
4845  */
4846  
4847
4848 /**
4849  * @class Roo.grid.ColumnModel
4850  * @extends Roo.util.Observable
4851  * This is the default implementation of a ColumnModel used by the Grid. It defines
4852  * the columns in the grid.
4853  * <br>Usage:<br>
4854  <pre><code>
4855  var colModel = new Roo.grid.ColumnModel([
4856         {header: "Ticker", width: 60, sortable: true, locked: true},
4857         {header: "Company Name", width: 150, sortable: true},
4858         {header: "Market Cap.", width: 100, sortable: true},
4859         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4860         {header: "Employees", width: 100, sortable: true, resizable: false}
4861  ]);
4862  </code></pre>
4863  * <p>
4864  
4865  * The config options listed for this class are options which may appear in each
4866  * individual column definition.
4867  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4868  * @constructor
4869  * @param {Object} config An Array of column config objects. See this class's
4870  * config objects for details.
4871 */
4872 Roo.grid.ColumnModel = function(config){
4873         /**
4874      * The config passed into the constructor
4875      */
4876     this.config = config;
4877     this.lookup = {};
4878
4879     // if no id, create one
4880     // if the column does not have a dataIndex mapping,
4881     // map it to the order it is in the config
4882     for(var i = 0, len = config.length; i < len; i++){
4883         var c = config[i];
4884         if(typeof c.dataIndex == "undefined"){
4885             c.dataIndex = i;
4886         }
4887         if(typeof c.renderer == "string"){
4888             c.renderer = Roo.util.Format[c.renderer];
4889         }
4890         if(typeof c.id == "undefined"){
4891             c.id = Roo.id();
4892         }
4893         if(c.editor && c.editor.xtype){
4894             c.editor  = Roo.factory(c.editor, Roo.grid);
4895         }
4896         if(c.editor && c.editor.isFormField){
4897             c.editor = new Roo.grid.GridEditor(c.editor);
4898         }
4899         this.lookup[c.id] = c;
4900     }
4901
4902     /**
4903      * The width of columns which have no width specified (defaults to 100)
4904      * @type Number
4905      */
4906     this.defaultWidth = 100;
4907
4908     /**
4909      * Default sortable of columns which have no sortable specified (defaults to false)
4910      * @type Boolean
4911      */
4912     this.defaultSortable = false;
4913
4914     this.addEvents({
4915         /**
4916              * @event widthchange
4917              * Fires when the width of a column changes.
4918              * @param {ColumnModel} this
4919              * @param {Number} columnIndex The column index
4920              * @param {Number} newWidth The new width
4921              */
4922             "widthchange": true,
4923         /**
4924              * @event headerchange
4925              * Fires when the text of a header changes.
4926              * @param {ColumnModel} this
4927              * @param {Number} columnIndex The column index
4928              * @param {Number} newText The new header text
4929              */
4930             "headerchange": true,
4931         /**
4932              * @event hiddenchange
4933              * Fires when a column is hidden or "unhidden".
4934              * @param {ColumnModel} this
4935              * @param {Number} columnIndex The column index
4936              * @param {Boolean} hidden true if hidden, false otherwise
4937              */
4938             "hiddenchange": true,
4939             /**
4940          * @event columnmoved
4941          * Fires when a column is moved.
4942          * @param {ColumnModel} this
4943          * @param {Number} oldIndex
4944          * @param {Number} newIndex
4945          */
4946         "columnmoved" : true,
4947         /**
4948          * @event columlockchange
4949          * Fires when a column's locked state is changed
4950          * @param {ColumnModel} this
4951          * @param {Number} colIndex
4952          * @param {Boolean} locked true if locked
4953          */
4954         "columnlockchange" : true
4955     });
4956     Roo.grid.ColumnModel.superclass.constructor.call(this);
4957 };
4958 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4959     /**
4960      * @cfg {String} header The header text to display in the Grid view.
4961      */
4962     /**
4963      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4964      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4965      * specified, the column's index is used as an index into the Record's data Array.
4966      */
4967     /**
4968      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4969      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4970      */
4971     /**
4972      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4973      * Defaults to the value of the {@link #defaultSortable} property.
4974      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4975      */
4976     /**
4977      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4978      */
4979     /**
4980      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4981      */
4982     /**
4983      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4984      */
4985     /**
4986      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4987      */
4988     /**
4989      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4990      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4991      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4992      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4993      */
4994        /**
4995      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4996      */
4997     /**
4998      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4999      */
5000     /**
5001      * @cfg {String} cursor (Optional)
5002      */
5003     /**
5004      * @cfg {String} tooltip (Optional)
5005      */
5006     /**
5007      * @cfg {Number} xs (Optional)
5008      */
5009     /**
5010      * @cfg {Number} sm (Optional)
5011      */
5012     /**
5013      * @cfg {Number} md (Optional)
5014      */
5015     /**
5016      * @cfg {Number} lg (Optional)
5017      */
5018     /**
5019      * Returns the id of the column at the specified index.
5020      * @param {Number} index The column index
5021      * @return {String} the id
5022      */
5023     getColumnId : function(index){
5024         return this.config[index].id;
5025     },
5026
5027     /**
5028      * Returns the column for a specified id.
5029      * @param {String} id The column id
5030      * @return {Object} the column
5031      */
5032     getColumnById : function(id){
5033         return this.lookup[id];
5034     },
5035
5036     
5037     /**
5038      * Returns the column for a specified dataIndex.
5039      * @param {String} dataIndex The column dataIndex
5040      * @return {Object|Boolean} the column or false if not found
5041      */
5042     getColumnByDataIndex: function(dataIndex){
5043         var index = this.findColumnIndex(dataIndex);
5044         return index > -1 ? this.config[index] : false;
5045     },
5046     
5047     /**
5048      * Returns the index for a specified column id.
5049      * @param {String} id The column id
5050      * @return {Number} the index, or -1 if not found
5051      */
5052     getIndexById : function(id){
5053         for(var i = 0, len = this.config.length; i < len; i++){
5054             if(this.config[i].id == id){
5055                 return i;
5056             }
5057         }
5058         return -1;
5059     },
5060     
5061     /**
5062      * Returns the index for a specified column dataIndex.
5063      * @param {String} dataIndex The column dataIndex
5064      * @return {Number} the index, or -1 if not found
5065      */
5066     
5067     findColumnIndex : function(dataIndex){
5068         for(var i = 0, len = this.config.length; i < len; i++){
5069             if(this.config[i].dataIndex == dataIndex){
5070                 return i;
5071             }
5072         }
5073         return -1;
5074     },
5075     
5076     
5077     moveColumn : function(oldIndex, newIndex){
5078         var c = this.config[oldIndex];
5079         this.config.splice(oldIndex, 1);
5080         this.config.splice(newIndex, 0, c);
5081         this.dataMap = null;
5082         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5083     },
5084
5085     isLocked : function(colIndex){
5086         return this.config[colIndex].locked === true;
5087     },
5088
5089     setLocked : function(colIndex, value, suppressEvent){
5090         if(this.isLocked(colIndex) == value){
5091             return;
5092         }
5093         this.config[colIndex].locked = value;
5094         if(!suppressEvent){
5095             this.fireEvent("columnlockchange", this, colIndex, value);
5096         }
5097     },
5098
5099     getTotalLockedWidth : function(){
5100         var totalWidth = 0;
5101         for(var i = 0; i < this.config.length; i++){
5102             if(this.isLocked(i) && !this.isHidden(i)){
5103                 this.totalWidth += this.getColumnWidth(i);
5104             }
5105         }
5106         return totalWidth;
5107     },
5108
5109     getLockedCount : function(){
5110         for(var i = 0, len = this.config.length; i < len; i++){
5111             if(!this.isLocked(i)){
5112                 return i;
5113             }
5114         }
5115     },
5116
5117     /**
5118      * Returns the number of columns.
5119      * @return {Number}
5120      */
5121     getColumnCount : function(visibleOnly){
5122         if(visibleOnly === true){
5123             var c = 0;
5124             for(var i = 0, len = this.config.length; i < len; i++){
5125                 if(!this.isHidden(i)){
5126                     c++;
5127                 }
5128             }
5129             return c;
5130         }
5131         return this.config.length;
5132     },
5133
5134     /**
5135      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5136      * @param {Function} fn
5137      * @param {Object} scope (optional)
5138      * @return {Array} result
5139      */
5140     getColumnsBy : function(fn, scope){
5141         var r = [];
5142         for(var i = 0, len = this.config.length; i < len; i++){
5143             var c = this.config[i];
5144             if(fn.call(scope||this, c, i) === true){
5145                 r[r.length] = c;
5146             }
5147         }
5148         return r;
5149     },
5150
5151     /**
5152      * Returns true if the specified column is sortable.
5153      * @param {Number} col The column index
5154      * @return {Boolean}
5155      */
5156     isSortable : function(col){
5157         if(typeof this.config[col].sortable == "undefined"){
5158             return this.defaultSortable;
5159         }
5160         return this.config[col].sortable;
5161     },
5162
5163     /**
5164      * Returns the rendering (formatting) function defined for the column.
5165      * @param {Number} col The column index.
5166      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5167      */
5168     getRenderer : function(col){
5169         if(!this.config[col].renderer){
5170             return Roo.grid.ColumnModel.defaultRenderer;
5171         }
5172         return this.config[col].renderer;
5173     },
5174
5175     /**
5176      * Sets the rendering (formatting) function for a column.
5177      * @param {Number} col The column index
5178      * @param {Function} fn The function to use to process the cell's raw data
5179      * to return HTML markup for the grid view. The render function is called with
5180      * the following parameters:<ul>
5181      * <li>Data value.</li>
5182      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5183      * <li>css A CSS style string to apply to the table cell.</li>
5184      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5185      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5186      * <li>Row index</li>
5187      * <li>Column index</li>
5188      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5189      */
5190     setRenderer : function(col, fn){
5191         this.config[col].renderer = fn;
5192     },
5193
5194     /**
5195      * Returns the width for the specified column.
5196      * @param {Number} col The column index
5197      * @return {Number}
5198      */
5199     getColumnWidth : function(col){
5200         return this.config[col].width * 1 || this.defaultWidth;
5201     },
5202
5203     /**
5204      * Sets the width for a column.
5205      * @param {Number} col The column index
5206      * @param {Number} width The new width
5207      */
5208     setColumnWidth : function(col, width, suppressEvent){
5209         this.config[col].width = width;
5210         this.totalWidth = null;
5211         if(!suppressEvent){
5212              this.fireEvent("widthchange", this, col, width);
5213         }
5214     },
5215
5216     /**
5217      * Returns the total width of all columns.
5218      * @param {Boolean} includeHidden True to include hidden column widths
5219      * @return {Number}
5220      */
5221     getTotalWidth : function(includeHidden){
5222         if(!this.totalWidth){
5223             this.totalWidth = 0;
5224             for(var i = 0, len = this.config.length; i < len; i++){
5225                 if(includeHidden || !this.isHidden(i)){
5226                     this.totalWidth += this.getColumnWidth(i);
5227                 }
5228             }
5229         }
5230         return this.totalWidth;
5231     },
5232
5233     /**
5234      * Returns the header for the specified column.
5235      * @param {Number} col The column index
5236      * @return {String}
5237      */
5238     getColumnHeader : function(col){
5239         return this.config[col].header;
5240     },
5241
5242     /**
5243      * Sets the header for a column.
5244      * @param {Number} col The column index
5245      * @param {String} header The new header
5246      */
5247     setColumnHeader : function(col, header){
5248         this.config[col].header = header;
5249         this.fireEvent("headerchange", this, col, header);
5250     },
5251
5252     /**
5253      * Returns the tooltip for the specified column.
5254      * @param {Number} col The column index
5255      * @return {String}
5256      */
5257     getColumnTooltip : function(col){
5258             return this.config[col].tooltip;
5259     },
5260     /**
5261      * Sets the tooltip for a column.
5262      * @param {Number} col The column index
5263      * @param {String} tooltip The new tooltip
5264      */
5265     setColumnTooltip : function(col, tooltip){
5266             this.config[col].tooltip = tooltip;
5267     },
5268
5269     /**
5270      * Returns the dataIndex for the specified column.
5271      * @param {Number} col The column index
5272      * @return {Number}
5273      */
5274     getDataIndex : function(col){
5275         return this.config[col].dataIndex;
5276     },
5277
5278     /**
5279      * Sets the dataIndex for a column.
5280      * @param {Number} col The column index
5281      * @param {Number} dataIndex The new dataIndex
5282      */
5283     setDataIndex : function(col, dataIndex){
5284         this.config[col].dataIndex = dataIndex;
5285     },
5286
5287     
5288     
5289     /**
5290      * Returns true if the cell is editable.
5291      * @param {Number} colIndex The column index
5292      * @param {Number} rowIndex The row index - this is nto actually used..?
5293      * @return {Boolean}
5294      */
5295     isCellEditable : function(colIndex, rowIndex){
5296         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5297     },
5298
5299     /**
5300      * Returns the editor defined for the cell/column.
5301      * return false or null to disable editing.
5302      * @param {Number} colIndex The column index
5303      * @param {Number} rowIndex The row index
5304      * @return {Object}
5305      */
5306     getCellEditor : function(colIndex, rowIndex){
5307         return this.config[colIndex].editor;
5308     },
5309
5310     /**
5311      * Sets if a column is editable.
5312      * @param {Number} col The column index
5313      * @param {Boolean} editable True if the column is editable
5314      */
5315     setEditable : function(col, editable){
5316         this.config[col].editable = editable;
5317     },
5318
5319
5320     /**
5321      * Returns true if the column is hidden.
5322      * @param {Number} colIndex The column index
5323      * @return {Boolean}
5324      */
5325     isHidden : function(colIndex){
5326         return this.config[colIndex].hidden;
5327     },
5328
5329
5330     /**
5331      * Returns true if the column width cannot be changed
5332      */
5333     isFixed : function(colIndex){
5334         return this.config[colIndex].fixed;
5335     },
5336
5337     /**
5338      * Returns true if the column can be resized
5339      * @return {Boolean}
5340      */
5341     isResizable : function(colIndex){
5342         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5343     },
5344     /**
5345      * Sets if a column is hidden.
5346      * @param {Number} colIndex The column index
5347      * @param {Boolean} hidden True if the column is hidden
5348      */
5349     setHidden : function(colIndex, hidden){
5350         this.config[colIndex].hidden = hidden;
5351         this.totalWidth = null;
5352         this.fireEvent("hiddenchange", this, colIndex, hidden);
5353     },
5354
5355     /**
5356      * Sets the editor for a column.
5357      * @param {Number} col The column index
5358      * @param {Object} editor The editor object
5359      */
5360     setEditor : function(col, editor){
5361         this.config[col].editor = editor;
5362     }
5363 });
5364
5365 Roo.grid.ColumnModel.defaultRenderer = function(value){
5366         if(typeof value == "string" && value.length < 1){
5367             return "&#160;";
5368         }
5369         return value;
5370 };
5371
5372 // Alias for backwards compatibility
5373 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5374 /*
5375  * Based on:
5376  * Ext JS Library 1.1.1
5377  * Copyright(c) 2006-2007, Ext JS, LLC.
5378  *
5379  * Originally Released Under LGPL - original licence link has changed is not relivant.
5380  *
5381  * Fork - LGPL
5382  * <script type="text/javascript">
5383  */
5384  
5385 /**
5386  * @class Roo.LoadMask
5387  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5388  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5389  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5390  * element's UpdateManager load indicator and will be destroyed after the initial load.
5391  * @constructor
5392  * Create a new LoadMask
5393  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5394  * @param {Object} config The config object
5395  */
5396 Roo.LoadMask = function(el, config){
5397     this.el = Roo.get(el);
5398     Roo.apply(this, config);
5399     if(this.store){
5400         this.store.on('beforeload', this.onBeforeLoad, this);
5401         this.store.on('load', this.onLoad, this);
5402         this.store.on('loadexception', this.onLoadException, this);
5403         this.removeMask = false;
5404     }else{
5405         var um = this.el.getUpdateManager();
5406         um.showLoadIndicator = false; // disable the default indicator
5407         um.on('beforeupdate', this.onBeforeLoad, this);
5408         um.on('update', this.onLoad, this);
5409         um.on('failure', this.onLoad, this);
5410         this.removeMask = true;
5411     }
5412 };
5413
5414 Roo.LoadMask.prototype = {
5415     /**
5416      * @cfg {Boolean} removeMask
5417      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5418      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5419      */
5420     /**
5421      * @cfg {String} msg
5422      * The text to display in a centered loading message box (defaults to 'Loading...')
5423      */
5424     msg : 'Loading...',
5425     /**
5426      * @cfg {String} msgCls
5427      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5428      */
5429     msgCls : 'x-mask-loading',
5430
5431     /**
5432      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5433      * @type Boolean
5434      */
5435     disabled: false,
5436
5437     /**
5438      * Disables the mask to prevent it from being displayed
5439      */
5440     disable : function(){
5441        this.disabled = true;
5442     },
5443
5444     /**
5445      * Enables the mask so that it can be displayed
5446      */
5447     enable : function(){
5448         this.disabled = false;
5449     },
5450     
5451     onLoadException : function()
5452     {
5453         Roo.log(arguments);
5454         
5455         if (typeof(arguments[3]) != 'undefined') {
5456             Roo.MessageBox.alert("Error loading",arguments[3]);
5457         } 
5458         /*
5459         try {
5460             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5461                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5462             }   
5463         } catch(e) {
5464             
5465         }
5466         */
5467     
5468         
5469         
5470         this.el.unmask(this.removeMask);
5471     },
5472     // private
5473     onLoad : function()
5474     {
5475         this.el.unmask(this.removeMask);
5476     },
5477
5478     // private
5479     onBeforeLoad : function(){
5480         if(!this.disabled){
5481             this.el.mask(this.msg, this.msgCls);
5482         }
5483     },
5484
5485     // private
5486     destroy : function(){
5487         if(this.store){
5488             this.store.un('beforeload', this.onBeforeLoad, this);
5489             this.store.un('load', this.onLoad, this);
5490             this.store.un('loadexception', this.onLoadException, this);
5491         }else{
5492             var um = this.el.getUpdateManager();
5493             um.un('beforeupdate', this.onBeforeLoad, this);
5494             um.un('update', this.onLoad, this);
5495             um.un('failure', this.onLoad, this);
5496         }
5497     }
5498 };/*
5499  * - LGPL
5500  *
5501  * table
5502  * 
5503  */
5504
5505 /**
5506  * @class Roo.bootstrap.Table
5507  * @extends Roo.bootstrap.Component
5508  * Bootstrap Table class
5509  * @cfg {String} cls table class
5510  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5511  * @cfg {String} bgcolor Specifies the background color for a table
5512  * @cfg {Number} border Specifies whether the table cells should have borders or not
5513  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5514  * @cfg {Number} cellspacing Specifies the space between cells
5515  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5516  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5517  * @cfg {String} sortable Specifies that the table should be sortable
5518  * @cfg {String} summary Specifies a summary of the content of a table
5519  * @cfg {Number} width Specifies the width of a table
5520  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5521  * 
5522  * @cfg {boolean} striped Should the rows be alternative striped
5523  * @cfg {boolean} bordered Add borders to the table
5524  * @cfg {boolean} hover Add hover highlighting
5525  * @cfg {boolean} condensed Format condensed
5526  * @cfg {boolean} responsive Format condensed
5527  * @cfg {Boolean} loadMask (true|false) default false
5528  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5529  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5530  * @cfg {Boolean} rowSelection (true|false) default false
5531  * @cfg {Boolean} cellSelection (true|false) default false
5532  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5533  
5534  * 
5535  * @constructor
5536  * Create a new Table
5537  * @param {Object} config The config object
5538  */
5539
5540 Roo.bootstrap.Table = function(config){
5541     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5542     
5543     // BC...
5544     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5545     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5546     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5547     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5548     
5549     
5550     if (this.sm) {
5551         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5552         this.sm = this.selModel;
5553         this.sm.xmodule = this.xmodule || false;
5554     }
5555     if (this.cm && typeof(this.cm.config) == 'undefined') {
5556         this.colModel = new Roo.grid.ColumnModel(this.cm);
5557         this.cm = this.colModel;
5558         this.cm.xmodule = this.xmodule || false;
5559     }
5560     if (this.store) {
5561         this.store= Roo.factory(this.store, Roo.data);
5562         this.ds = this.store;
5563         this.ds.xmodule = this.xmodule || false;
5564          
5565     }
5566     if (this.footer && this.store) {
5567         this.footer.dataSource = this.ds;
5568         this.footer = Roo.factory(this.footer);
5569     }
5570     
5571     /** @private */
5572     this.addEvents({
5573         /**
5574          * @event cellclick
5575          * Fires when a cell is clicked
5576          * @param {Roo.bootstrap.Table} this
5577          * @param {Roo.Element} el
5578          * @param {Number} rowIndex
5579          * @param {Number} columnIndex
5580          * @param {Roo.EventObject} e
5581          */
5582         "cellclick" : true,
5583         /**
5584          * @event celldblclick
5585          * Fires when a cell is double clicked
5586          * @param {Roo.bootstrap.Table} this
5587          * @param {Roo.Element} el
5588          * @param {Number} rowIndex
5589          * @param {Number} columnIndex
5590          * @param {Roo.EventObject} e
5591          */
5592         "celldblclick" : true,
5593         /**
5594          * @event rowclick
5595          * Fires when a row is clicked
5596          * @param {Roo.bootstrap.Table} this
5597          * @param {Roo.Element} el
5598          * @param {Number} rowIndex
5599          * @param {Roo.EventObject} e
5600          */
5601         "rowclick" : true,
5602         /**
5603          * @event rowdblclick
5604          * Fires when a row is double clicked
5605          * @param {Roo.bootstrap.Table} this
5606          * @param {Roo.Element} el
5607          * @param {Number} rowIndex
5608          * @param {Roo.EventObject} e
5609          */
5610         "rowdblclick" : true,
5611         /**
5612          * @event mouseover
5613          * Fires when a mouseover occur
5614          * @param {Roo.bootstrap.Table} this
5615          * @param {Roo.Element} el
5616          * @param {Number} rowIndex
5617          * @param {Number} columnIndex
5618          * @param {Roo.EventObject} e
5619          */
5620         "mouseover" : true,
5621         /**
5622          * @event mouseout
5623          * Fires when a mouseout occur
5624          * @param {Roo.bootstrap.Table} this
5625          * @param {Roo.Element} el
5626          * @param {Number} rowIndex
5627          * @param {Number} columnIndex
5628          * @param {Roo.EventObject} e
5629          */
5630         "mouseout" : true,
5631         /**
5632          * @event rowclass
5633          * Fires when a row is rendered, so you can change add a style to it.
5634          * @param {Roo.bootstrap.Table} this
5635          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5636          */
5637         'rowclass' : true,
5638           /**
5639          * @event rowsrendered
5640          * Fires when all the  rows have been rendered
5641          * @param {Roo.bootstrap.Table} this
5642          */
5643         'rowsrendered' : true
5644         
5645     });
5646 };
5647
5648 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5649     
5650     cls: false,
5651     align: false,
5652     bgcolor: false,
5653     border: false,
5654     cellpadding: false,
5655     cellspacing: false,
5656     frame: false,
5657     rules: false,
5658     sortable: false,
5659     summary: false,
5660     width: false,
5661     striped : false,
5662     bordered: false,
5663     hover:  false,
5664     condensed : false,
5665     responsive : false,
5666     sm : false,
5667     cm : false,
5668     store : false,
5669     loadMask : false,
5670     footerShow : true,
5671     headerShow : true,
5672   
5673     rowSelection : false,
5674     cellSelection : false,
5675     layout : false,
5676     
5677     // Roo.Element - the tbody
5678     mainBody: false, 
5679     
5680     getAutoCreate : function(){
5681         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5682         
5683         cfg = {
5684             tag: 'table',
5685             cls : 'table',
5686             cn : []
5687         };
5688             
5689         if (this.striped) {
5690             cfg.cls += ' table-striped';
5691         }
5692         
5693         if (this.hover) {
5694             cfg.cls += ' table-hover';
5695         }
5696         if (this.bordered) {
5697             cfg.cls += ' table-bordered';
5698         }
5699         if (this.condensed) {
5700             cfg.cls += ' table-condensed';
5701         }
5702         if (this.responsive) {
5703             cfg.cls += ' table-responsive';
5704         }
5705         
5706         if (this.cls) {
5707             cfg.cls+=  ' ' +this.cls;
5708         }
5709         
5710         // this lot should be simplifed...
5711         
5712         if (this.align) {
5713             cfg.align=this.align;
5714         }
5715         if (this.bgcolor) {
5716             cfg.bgcolor=this.bgcolor;
5717         }
5718         if (this.border) {
5719             cfg.border=this.border;
5720         }
5721         if (this.cellpadding) {
5722             cfg.cellpadding=this.cellpadding;
5723         }
5724         if (this.cellspacing) {
5725             cfg.cellspacing=this.cellspacing;
5726         }
5727         if (this.frame) {
5728             cfg.frame=this.frame;
5729         }
5730         if (this.rules) {
5731             cfg.rules=this.rules;
5732         }
5733         if (this.sortable) {
5734             cfg.sortable=this.sortable;
5735         }
5736         if (this.summary) {
5737             cfg.summary=this.summary;
5738         }
5739         if (this.width) {
5740             cfg.width=this.width;
5741         }
5742         if (this.layout) {
5743             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5744         }
5745         
5746         if(this.store || this.cm){
5747             if(this.headerShow){
5748                 cfg.cn.push(this.renderHeader());
5749             }
5750             
5751             cfg.cn.push(this.renderBody());
5752             
5753             if(this.footerShow){
5754                 cfg.cn.push(this.renderFooter());
5755             }
5756             
5757             cfg.cls+=  ' TableGrid';
5758         }
5759         
5760         return { cn : [ cfg ] };
5761     },
5762     
5763     initEvents : function()
5764     {   
5765         if(!this.store || !this.cm){
5766             return;
5767         }
5768         
5769         //Roo.log('initEvents with ds!!!!');
5770         
5771         this.mainBody = this.el.select('tbody', true).first();
5772         
5773         
5774         var _this = this;
5775         
5776         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5777             e.on('click', _this.sort, _this);
5778         });
5779         
5780         this.el.on("click", this.onClick, this);
5781         this.el.on("dblclick", this.onDblClick, this);
5782         
5783         // why is this done????? = it breaks dialogs??
5784         //this.parent().el.setStyle('position', 'relative');
5785         
5786         
5787         if (this.footer) {
5788             this.footer.parentId = this.id;
5789             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5790         }
5791         
5792         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5793         
5794         this.store.on('load', this.onLoad, this);
5795         this.store.on('beforeload', this.onBeforeLoad, this);
5796         this.store.on('update', this.onUpdate, this);
5797         this.store.on('add', this.onAdd, this);
5798         
5799     },
5800     
5801     onMouseover : function(e, el)
5802     {
5803         var cell = Roo.get(el);
5804         
5805         if(!cell){
5806             return;
5807         }
5808         
5809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810             cell = cell.findParent('td', false, true);
5811         }
5812         
5813         var row = cell.findParent('tr', false, true);
5814         var cellIndex = cell.dom.cellIndex;
5815         var rowIndex = row.dom.rowIndex - 1; // start from 0
5816         
5817         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5818         
5819     },
5820     
5821     onMouseout : function(e, el)
5822     {
5823         var cell = Roo.get(el);
5824         
5825         if(!cell){
5826             return;
5827         }
5828         
5829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830             cell = cell.findParent('td', false, true);
5831         }
5832         
5833         var row = cell.findParent('tr', false, true);
5834         var cellIndex = cell.dom.cellIndex;
5835         var rowIndex = row.dom.rowIndex - 1; // start from 0
5836         
5837         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5838         
5839     },
5840     
5841     onClick : function(e, el)
5842     {
5843         var cell = Roo.get(el);
5844         
5845         if(!cell || (!this.cellSelection && !this.rowSelection)){
5846             return;
5847         }
5848         
5849         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5850             cell = cell.findParent('td', false, true);
5851         }
5852         
5853         if(!cell || typeof(cell) == 'undefined'){
5854             return;
5855         }
5856         
5857         var row = cell.findParent('tr', false, true);
5858         
5859         if(!row || typeof(row) == 'undefined'){
5860             return;
5861         }
5862         
5863         var cellIndex = cell.dom.cellIndex;
5864         var rowIndex = this.getRowIndex(row);
5865         
5866         // why??? - should these not be based on SelectionModel?
5867         if(this.cellSelection){
5868             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5869         }
5870         
5871         if(this.rowSelection){
5872             this.fireEvent('rowclick', this, row, rowIndex, e);
5873         }
5874         
5875         
5876     },
5877     
5878     onDblClick : function(e,el)
5879     {
5880         var cell = Roo.get(el);
5881         
5882         if(!cell || (!this.CellSelection && !this.RowSelection)){
5883             return;
5884         }
5885         
5886         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5887             cell = cell.findParent('td', false, true);
5888         }
5889         
5890         if(!cell || typeof(cell) == 'undefined'){
5891             return;
5892         }
5893         
5894         var row = cell.findParent('tr', false, true);
5895         
5896         if(!row || typeof(row) == 'undefined'){
5897             return;
5898         }
5899         
5900         var cellIndex = cell.dom.cellIndex;
5901         var rowIndex = this.getRowIndex(row);
5902         
5903         if(this.CellSelection){
5904             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5905         }
5906         
5907         if(this.RowSelection){
5908             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5909         }
5910     },
5911     
5912     sort : function(e,el)
5913     {
5914         var col = Roo.get(el);
5915         
5916         if(!col.hasClass('sortable')){
5917             return;
5918         }
5919         
5920         var sort = col.attr('sort');
5921         var dir = 'ASC';
5922         
5923         if(col.hasClass('glyphicon-arrow-up')){
5924             dir = 'DESC';
5925         }
5926         
5927         this.store.sortInfo = {field : sort, direction : dir};
5928         
5929         if (this.footer) {
5930             Roo.log("calling footer first");
5931             this.footer.onClick('first');
5932         } else {
5933         
5934             this.store.load({ params : { start : 0 } });
5935         }
5936     },
5937     
5938     renderHeader : function()
5939     {
5940         var header = {
5941             tag: 'thead',
5942             cn : []
5943         };
5944         
5945         var cm = this.cm;
5946         
5947         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5948             
5949             var config = cm.config[i];
5950             
5951             var c = {
5952                 tag: 'th',
5953                 style : '',
5954                 html: cm.getColumnHeader(i)
5955             };
5956             
5957             var hh = '';
5958             
5959             if(typeof(config.lgHeader) != 'undefined'){
5960                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5961             }
5962             
5963             if(typeof(config.mdHeader) != 'undefined'){
5964                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5965             }
5966             
5967             if(typeof(config.smHeader) != 'undefined'){
5968                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5969             }
5970             
5971             if(typeof(config.xsHeader) != 'undefined'){
5972                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5973             }
5974             
5975             if(hh.length){
5976                 c.html = hh;
5977             }
5978             
5979             if(typeof(config.tooltip) != 'undefined'){
5980                 c.tooltip = config.tooltip;
5981             }
5982             
5983             if(typeof(config.colspan) != 'undefined'){
5984                 c.colspan = config.colspan;
5985             }
5986             
5987             if(typeof(config.hidden) != 'undefined' && config.hidden){
5988                 c.style += ' display:none;';
5989             }
5990             
5991             if(typeof(config.dataIndex) != 'undefined'){
5992                 c.sort = config.dataIndex;
5993             }
5994             
5995             if(typeof(config.sortable) != 'undefined' && config.sortable){
5996                 c.cls = 'sortable';
5997             }
5998             
5999             if(typeof(config.align) != 'undefined' && config.align.length){
6000                 c.style += ' text-align:' + config.align + ';';
6001             }
6002             
6003             if(typeof(config.width) != 'undefined'){
6004                 c.style += ' width:' + config.width + 'px;';
6005             }
6006             
6007             if(typeof(config.cls) != 'undefined'){
6008                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6009             }
6010             
6011             ['xs','sm','md','lg'].map(function(size){
6012                 
6013                 if(typeof(config[size]) == 'undefined'){
6014                     return;
6015                 }
6016                 
6017                 if (!config[size]) { // 0 = hidden
6018                     c.cls += ' hidden-' + size;
6019                     return;
6020                 }
6021                 
6022                 c.cls += ' col-' + size + '-' + config[size];
6023
6024             });
6025             
6026             header.cn.push(c)
6027         }
6028         
6029         return header;
6030     },
6031     
6032     renderBody : function()
6033     {
6034         var body = {
6035             tag: 'tbody',
6036             cn : [
6037                 {
6038                     tag: 'tr',
6039                     cn : [
6040                         {
6041                             tag : 'td',
6042                             colspan :  this.cm.getColumnCount()
6043                         }
6044                     ]
6045                 }
6046             ]
6047         };
6048         
6049         return body;
6050     },
6051     
6052     renderFooter : function()
6053     {
6054         var footer = {
6055             tag: 'tfoot',
6056             cn : [
6057                 {
6058                     tag: 'tr',
6059                     cn : [
6060                         {
6061                             tag : 'td',
6062                             colspan :  this.cm.getColumnCount()
6063                         }
6064                     ]
6065                 }
6066             ]
6067         };
6068         
6069         return footer;
6070     },
6071     
6072     
6073     
6074     onLoad : function()
6075     {
6076         Roo.log('ds onload');
6077         this.clear();
6078         
6079         var _this = this;
6080         var cm = this.cm;
6081         var ds = this.store;
6082         
6083         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6084             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6085             
6086             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6087                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6088             }
6089             
6090             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6091                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6092             }
6093         });
6094         
6095         var tbody =  this.mainBody;
6096               
6097         if(ds.getCount() > 0){
6098             ds.data.each(function(d,rowIndex){
6099                 var row =  this.renderRow(cm, ds, rowIndex);
6100                 
6101                 tbody.createChild(row);
6102                 
6103                 var _this = this;
6104                 
6105                 if(row.cellObjects.length){
6106                     Roo.each(row.cellObjects, function(r){
6107                         _this.renderCellObject(r);
6108                     })
6109                 }
6110                 
6111             }, this);
6112         }
6113         
6114         Roo.each(this.el.select('tbody td', true).elements, function(e){
6115             e.on('mouseover', _this.onMouseover, _this);
6116         });
6117         
6118         Roo.each(this.el.select('tbody td', true).elements, function(e){
6119             e.on('mouseout', _this.onMouseout, _this);
6120         });
6121         this.fireEvent('rowsrendered', this);
6122         //if(this.loadMask){
6123         //    this.maskEl.hide();
6124         //}
6125     },
6126     
6127     
6128     onUpdate : function(ds,record)
6129     {
6130         this.refreshRow(record);
6131     },
6132     
6133     onRemove : function(ds, record, index, isUpdate){
6134         if(isUpdate !== true){
6135             this.fireEvent("beforerowremoved", this, index, record);
6136         }
6137         var bt = this.mainBody.dom;
6138         
6139         var rows = this.el.select('tbody > tr', true).elements;
6140         
6141         if(typeof(rows[index]) != 'undefined'){
6142             bt.removeChild(rows[index].dom);
6143         }
6144         
6145 //        if(bt.rows[index]){
6146 //            bt.removeChild(bt.rows[index]);
6147 //        }
6148         
6149         if(isUpdate !== true){
6150             //this.stripeRows(index);
6151             //this.syncRowHeights(index, index);
6152             //this.layout();
6153             this.fireEvent("rowremoved", this, index, record);
6154         }
6155     },
6156     
6157     onAdd : function(ds, records, rowIndex)
6158     {
6159         //Roo.log('on Add called');
6160         // - note this does not handle multiple adding very well..
6161         var bt = this.mainBody.dom;
6162         for (var i =0 ; i < records.length;i++) {
6163             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6164             //Roo.log(records[i]);
6165             //Roo.log(this.store.getAt(rowIndex+i));
6166             this.insertRow(this.store, rowIndex + i, false);
6167             return;
6168         }
6169         
6170     },
6171     
6172     
6173     refreshRow : function(record){
6174         var ds = this.store, index;
6175         if(typeof record == 'number'){
6176             index = record;
6177             record = ds.getAt(index);
6178         }else{
6179             index = ds.indexOf(record);
6180         }
6181         this.insertRow(ds, index, true);
6182         this.onRemove(ds, record, index+1, true);
6183         //this.syncRowHeights(index, index);
6184         //this.layout();
6185         this.fireEvent("rowupdated", this, index, record);
6186     },
6187     
6188     insertRow : function(dm, rowIndex, isUpdate){
6189         
6190         if(!isUpdate){
6191             this.fireEvent("beforerowsinserted", this, rowIndex);
6192         }
6193             //var s = this.getScrollState();
6194         var row = this.renderRow(this.cm, this.store, rowIndex);
6195         // insert before rowIndex..
6196         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6197         
6198         var _this = this;
6199                 
6200         if(row.cellObjects.length){
6201             Roo.each(row.cellObjects, function(r){
6202                 _this.renderCellObject(r);
6203             })
6204         }
6205             
6206         if(!isUpdate){
6207             this.fireEvent("rowsinserted", this, rowIndex);
6208             //this.syncRowHeights(firstRow, lastRow);
6209             //this.stripeRows(firstRow);
6210             //this.layout();
6211         }
6212         
6213     },
6214     
6215     
6216     getRowDom : function(rowIndex)
6217     {
6218         var rows = this.el.select('tbody > tr', true).elements;
6219         
6220         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6221         
6222     },
6223     // returns the object tree for a tr..
6224   
6225     
6226     renderRow : function(cm, ds, rowIndex) 
6227     {
6228         
6229         var d = ds.getAt(rowIndex);
6230         
6231         var row = {
6232             tag : 'tr',
6233             cn : []
6234         };
6235             
6236         var cellObjects = [];
6237         
6238         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6239             var config = cm.config[i];
6240             
6241             var renderer = cm.getRenderer(i);
6242             var value = '';
6243             var id = false;
6244             
6245             if(typeof(renderer) !== 'undefined'){
6246                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6247             }
6248             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6249             // and are rendered into the cells after the row is rendered - using the id for the element.
6250             
6251             if(typeof(value) === 'object'){
6252                 id = Roo.id();
6253                 cellObjects.push({
6254                     container : id,
6255                     cfg : value 
6256                 })
6257             }
6258             
6259             var rowcfg = {
6260                 record: d,
6261                 rowIndex : rowIndex,
6262                 colIndex : i,
6263                 rowClass : ''
6264             };
6265
6266             this.fireEvent('rowclass', this, rowcfg);
6267             
6268             var td = {
6269                 tag: 'td',
6270                 cls : rowcfg.rowClass,
6271                 style: '',
6272                 html: (typeof(value) === 'object') ? '' : value
6273             };
6274             
6275             if (id) {
6276                 td.id = id;
6277             }
6278             
6279             if(typeof(config.colspan) != 'undefined'){
6280                 td.colspan = config.colspan;
6281             }
6282             
6283             if(typeof(config.hidden) != 'undefined' && config.hidden){
6284                 td.style += ' display:none;';
6285             }
6286             
6287             if(typeof(config.align) != 'undefined' && config.align.length){
6288                 td.style += ' text-align:' + config.align + ';';
6289             }
6290             
6291             if(typeof(config.width) != 'undefined'){
6292                 td.style += ' width:' +  config.width + 'px;';
6293             }
6294             
6295             if(typeof(config.cursor) != 'undefined'){
6296                 td.style += ' cursor:' +  config.cursor + ';';
6297             }
6298             
6299             if(typeof(config.cls) != 'undefined'){
6300                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6301             }
6302             
6303             ['xs','sm','md','lg'].map(function(size){
6304                 
6305                 if(typeof(config[size]) == 'undefined'){
6306                     return;
6307                 }
6308                 
6309                 if (!config[size]) { // 0 = hidden
6310                     td.cls += ' hidden-' + size;
6311                     return;
6312                 }
6313                 
6314                 td.cls += ' col-' + size + '-' + config[size];
6315
6316             });
6317              
6318             row.cn.push(td);
6319            
6320         }
6321         
6322         row.cellObjects = cellObjects;
6323         
6324         return row;
6325           
6326     },
6327     
6328     
6329     
6330     onBeforeLoad : function()
6331     {
6332         //Roo.log('ds onBeforeLoad');
6333         
6334         //this.clear();
6335         
6336         //if(this.loadMask){
6337         //    this.maskEl.show();
6338         //}
6339     },
6340      /**
6341      * Remove all rows
6342      */
6343     clear : function()
6344     {
6345         this.el.select('tbody', true).first().dom.innerHTML = '';
6346     },
6347     /**
6348      * Show or hide a row.
6349      * @param {Number} rowIndex to show or hide
6350      * @param {Boolean} state hide
6351      */
6352     setRowVisibility : function(rowIndex, state)
6353     {
6354         var bt = this.mainBody.dom;
6355         
6356         var rows = this.el.select('tbody > tr', true).elements;
6357         
6358         if(typeof(rows[rowIndex]) == 'undefined'){
6359             return;
6360         }
6361         rows[rowIndex].dom.style.display = state ? '' : 'none';
6362     },
6363     
6364     
6365     getSelectionModel : function(){
6366         if(!this.selModel){
6367             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6368         }
6369         return this.selModel;
6370     },
6371     /*
6372      * Render the Roo.bootstrap object from renderder
6373      */
6374     renderCellObject : function(r)
6375     {
6376         var _this = this;
6377         
6378         var t = r.cfg.render(r.container);
6379         
6380         if(r.cfg.cn){
6381             Roo.each(r.cfg.cn, function(c){
6382                 var child = {
6383                     container: t.getChildContainer(),
6384                     cfg: c
6385                 };
6386                 _this.renderCellObject(child);
6387             })
6388         }
6389     },
6390     
6391     getRowIndex : function(row)
6392     {
6393         var rowIndex = -1;
6394         
6395         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6396             if(el != row){
6397                 return;
6398             }
6399             
6400             rowIndex = index;
6401         });
6402         
6403         return rowIndex;
6404     }
6405    
6406 });
6407
6408  
6409
6410  /*
6411  * - LGPL
6412  *
6413  * table cell
6414  * 
6415  */
6416
6417 /**
6418  * @class Roo.bootstrap.TableCell
6419  * @extends Roo.bootstrap.Component
6420  * Bootstrap TableCell class
6421  * @cfg {String} html cell contain text
6422  * @cfg {String} cls cell class
6423  * @cfg {String} tag cell tag (td|th) default td
6424  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6425  * @cfg {String} align Aligns the content in a cell
6426  * @cfg {String} axis Categorizes cells
6427  * @cfg {String} bgcolor Specifies the background color of a cell
6428  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6429  * @cfg {Number} colspan Specifies the number of columns a cell should span
6430  * @cfg {String} headers Specifies one or more header cells a cell is related to
6431  * @cfg {Number} height Sets the height of a cell
6432  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6433  * @cfg {Number} rowspan Sets the number of rows a cell should span
6434  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6435  * @cfg {String} valign Vertical aligns the content in a cell
6436  * @cfg {Number} width Specifies the width of a cell
6437  * 
6438  * @constructor
6439  * Create a new TableCell
6440  * @param {Object} config The config object
6441  */
6442
6443 Roo.bootstrap.TableCell = function(config){
6444     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6445 };
6446
6447 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6448     
6449     html: false,
6450     cls: false,
6451     tag: false,
6452     abbr: false,
6453     align: false,
6454     axis: false,
6455     bgcolor: false,
6456     charoff: false,
6457     colspan: false,
6458     headers: false,
6459     height: false,
6460     nowrap: false,
6461     rowspan: false,
6462     scope: false,
6463     valign: false,
6464     width: false,
6465     
6466     
6467     getAutoCreate : function(){
6468         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6469         
6470         cfg = {
6471             tag: 'td'
6472         };
6473         
6474         if(this.tag){
6475             cfg.tag = this.tag;
6476         }
6477         
6478         if (this.html) {
6479             cfg.html=this.html
6480         }
6481         if (this.cls) {
6482             cfg.cls=this.cls
6483         }
6484         if (this.abbr) {
6485             cfg.abbr=this.abbr
6486         }
6487         if (this.align) {
6488             cfg.align=this.align
6489         }
6490         if (this.axis) {
6491             cfg.axis=this.axis
6492         }
6493         if (this.bgcolor) {
6494             cfg.bgcolor=this.bgcolor
6495         }
6496         if (this.charoff) {
6497             cfg.charoff=this.charoff
6498         }
6499         if (this.colspan) {
6500             cfg.colspan=this.colspan
6501         }
6502         if (this.headers) {
6503             cfg.headers=this.headers
6504         }
6505         if (this.height) {
6506             cfg.height=this.height
6507         }
6508         if (this.nowrap) {
6509             cfg.nowrap=this.nowrap
6510         }
6511         if (this.rowspan) {
6512             cfg.rowspan=this.rowspan
6513         }
6514         if (this.scope) {
6515             cfg.scope=this.scope
6516         }
6517         if (this.valign) {
6518             cfg.valign=this.valign
6519         }
6520         if (this.width) {
6521             cfg.width=this.width
6522         }
6523         
6524         
6525         return cfg;
6526     }
6527    
6528 });
6529
6530  
6531
6532  /*
6533  * - LGPL
6534  *
6535  * table row
6536  * 
6537  */
6538
6539 /**
6540  * @class Roo.bootstrap.TableRow
6541  * @extends Roo.bootstrap.Component
6542  * Bootstrap TableRow class
6543  * @cfg {String} cls row class
6544  * @cfg {String} align Aligns the content in a table row
6545  * @cfg {String} bgcolor Specifies a background color for a table row
6546  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6547  * @cfg {String} valign Vertical aligns the content in a table row
6548  * 
6549  * @constructor
6550  * Create a new TableRow
6551  * @param {Object} config The config object
6552  */
6553
6554 Roo.bootstrap.TableRow = function(config){
6555     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6556 };
6557
6558 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6559     
6560     cls: false,
6561     align: false,
6562     bgcolor: false,
6563     charoff: false,
6564     valign: false,
6565     
6566     getAutoCreate : function(){
6567         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6568         
6569         cfg = {
6570             tag: 'tr'
6571         };
6572             
6573         if(this.cls){
6574             cfg.cls = this.cls;
6575         }
6576         if(this.align){
6577             cfg.align = this.align;
6578         }
6579         if(this.bgcolor){
6580             cfg.bgcolor = this.bgcolor;
6581         }
6582         if(this.charoff){
6583             cfg.charoff = this.charoff;
6584         }
6585         if(this.valign){
6586             cfg.valign = this.valign;
6587         }
6588         
6589         return cfg;
6590     }
6591    
6592 });
6593
6594  
6595
6596  /*
6597  * - LGPL
6598  *
6599  * table body
6600  * 
6601  */
6602
6603 /**
6604  * @class Roo.bootstrap.TableBody
6605  * @extends Roo.bootstrap.Component
6606  * Bootstrap TableBody class
6607  * @cfg {String} cls element class
6608  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6609  * @cfg {String} align Aligns the content inside the element
6610  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6611  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6612  * 
6613  * @constructor
6614  * Create a new TableBody
6615  * @param {Object} config The config object
6616  */
6617
6618 Roo.bootstrap.TableBody = function(config){
6619     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6620 };
6621
6622 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6623     
6624     cls: false,
6625     tag: false,
6626     align: false,
6627     charoff: false,
6628     valign: false,
6629     
6630     getAutoCreate : function(){
6631         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6632         
6633         cfg = {
6634             tag: 'tbody'
6635         };
6636             
6637         if (this.cls) {
6638             cfg.cls=this.cls
6639         }
6640         if(this.tag){
6641             cfg.tag = this.tag;
6642         }
6643         
6644         if(this.align){
6645             cfg.align = this.align;
6646         }
6647         if(this.charoff){
6648             cfg.charoff = this.charoff;
6649         }
6650         if(this.valign){
6651             cfg.valign = this.valign;
6652         }
6653         
6654         return cfg;
6655     }
6656     
6657     
6658 //    initEvents : function()
6659 //    {
6660 //        
6661 //        if(!this.store){
6662 //            return;
6663 //        }
6664 //        
6665 //        this.store = Roo.factory(this.store, Roo.data);
6666 //        this.store.on('load', this.onLoad, this);
6667 //        
6668 //        this.store.load();
6669 //        
6670 //    },
6671 //    
6672 //    onLoad: function () 
6673 //    {   
6674 //        this.fireEvent('load', this);
6675 //    }
6676 //    
6677 //   
6678 });
6679
6680  
6681
6682  /*
6683  * Based on:
6684  * Ext JS Library 1.1.1
6685  * Copyright(c) 2006-2007, Ext JS, LLC.
6686  *
6687  * Originally Released Under LGPL - original licence link has changed is not relivant.
6688  *
6689  * Fork - LGPL
6690  * <script type="text/javascript">
6691  */
6692
6693 // as we use this in bootstrap.
6694 Roo.namespace('Roo.form');
6695  /**
6696  * @class Roo.form.Action
6697  * Internal Class used to handle form actions
6698  * @constructor
6699  * @param {Roo.form.BasicForm} el The form element or its id
6700  * @param {Object} config Configuration options
6701  */
6702
6703  
6704  
6705 // define the action interface
6706 Roo.form.Action = function(form, options){
6707     this.form = form;
6708     this.options = options || {};
6709 };
6710 /**
6711  * Client Validation Failed
6712  * @const 
6713  */
6714 Roo.form.Action.CLIENT_INVALID = 'client';
6715 /**
6716  * Server Validation Failed
6717  * @const 
6718  */
6719 Roo.form.Action.SERVER_INVALID = 'server';
6720  /**
6721  * Connect to Server Failed
6722  * @const 
6723  */
6724 Roo.form.Action.CONNECT_FAILURE = 'connect';
6725 /**
6726  * Reading Data from Server Failed
6727  * @const 
6728  */
6729 Roo.form.Action.LOAD_FAILURE = 'load';
6730
6731 Roo.form.Action.prototype = {
6732     type : 'default',
6733     failureType : undefined,
6734     response : undefined,
6735     result : undefined,
6736
6737     // interface method
6738     run : function(options){
6739
6740     },
6741
6742     // interface method
6743     success : function(response){
6744
6745     },
6746
6747     // interface method
6748     handleResponse : function(response){
6749
6750     },
6751
6752     // default connection failure
6753     failure : function(response){
6754         
6755         this.response = response;
6756         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6757         this.form.afterAction(this, false);
6758     },
6759
6760     processResponse : function(response){
6761         this.response = response;
6762         if(!response.responseText){
6763             return true;
6764         }
6765         this.result = this.handleResponse(response);
6766         return this.result;
6767     },
6768
6769     // utility functions used internally
6770     getUrl : function(appendParams){
6771         var url = this.options.url || this.form.url || this.form.el.dom.action;
6772         if(appendParams){
6773             var p = this.getParams();
6774             if(p){
6775                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6776             }
6777         }
6778         return url;
6779     },
6780
6781     getMethod : function(){
6782         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6783     },
6784
6785     getParams : function(){
6786         var bp = this.form.baseParams;
6787         var p = this.options.params;
6788         if(p){
6789             if(typeof p == "object"){
6790                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6791             }else if(typeof p == 'string' && bp){
6792                 p += '&' + Roo.urlEncode(bp);
6793             }
6794         }else if(bp){
6795             p = Roo.urlEncode(bp);
6796         }
6797         return p;
6798     },
6799
6800     createCallback : function(){
6801         return {
6802             success: this.success,
6803             failure: this.failure,
6804             scope: this,
6805             timeout: (this.form.timeout*1000),
6806             upload: this.form.fileUpload ? this.success : undefined
6807         };
6808     }
6809 };
6810
6811 Roo.form.Action.Submit = function(form, options){
6812     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6813 };
6814
6815 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6816     type : 'submit',
6817
6818     haveProgress : false,
6819     uploadComplete : false,
6820     
6821     // uploadProgress indicator.
6822     uploadProgress : function()
6823     {
6824         if (!this.form.progressUrl) {
6825             return;
6826         }
6827         
6828         if (!this.haveProgress) {
6829             Roo.MessageBox.progress("Uploading", "Uploading");
6830         }
6831         if (this.uploadComplete) {
6832            Roo.MessageBox.hide();
6833            return;
6834         }
6835         
6836         this.haveProgress = true;
6837    
6838         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6839         
6840         var c = new Roo.data.Connection();
6841         c.request({
6842             url : this.form.progressUrl,
6843             params: {
6844                 id : uid
6845             },
6846             method: 'GET',
6847             success : function(req){
6848                //console.log(data);
6849                 var rdata = false;
6850                 var edata;
6851                 try  {
6852                    rdata = Roo.decode(req.responseText)
6853                 } catch (e) {
6854                     Roo.log("Invalid data from server..");
6855                     Roo.log(edata);
6856                     return;
6857                 }
6858                 if (!rdata || !rdata.success) {
6859                     Roo.log(rdata);
6860                     Roo.MessageBox.alert(Roo.encode(rdata));
6861                     return;
6862                 }
6863                 var data = rdata.data;
6864                 
6865                 if (this.uploadComplete) {
6866                    Roo.MessageBox.hide();
6867                    return;
6868                 }
6869                    
6870                 if (data){
6871                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6872                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6873                     );
6874                 }
6875                 this.uploadProgress.defer(2000,this);
6876             },
6877        
6878             failure: function(data) {
6879                 Roo.log('progress url failed ');
6880                 Roo.log(data);
6881             },
6882             scope : this
6883         });
6884            
6885     },
6886     
6887     
6888     run : function()
6889     {
6890         // run get Values on the form, so it syncs any secondary forms.
6891         this.form.getValues();
6892         
6893         var o = this.options;
6894         var method = this.getMethod();
6895         var isPost = method == 'POST';
6896         if(o.clientValidation === false || this.form.isValid()){
6897             
6898             if (this.form.progressUrl) {
6899                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6900                     (new Date() * 1) + '' + Math.random());
6901                     
6902             } 
6903             
6904             
6905             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6906                 form:this.form.el.dom,
6907                 url:this.getUrl(!isPost),
6908                 method: method,
6909                 params:isPost ? this.getParams() : null,
6910                 isUpload: this.form.fileUpload
6911             }));
6912             
6913             this.uploadProgress();
6914
6915         }else if (o.clientValidation !== false){ // client validation failed
6916             this.failureType = Roo.form.Action.CLIENT_INVALID;
6917             this.form.afterAction(this, false);
6918         }
6919     },
6920
6921     success : function(response)
6922     {
6923         this.uploadComplete= true;
6924         if (this.haveProgress) {
6925             Roo.MessageBox.hide();
6926         }
6927         
6928         
6929         var result = this.processResponse(response);
6930         if(result === true || result.success){
6931             this.form.afterAction(this, true);
6932             return;
6933         }
6934         if(result.errors){
6935             this.form.markInvalid(result.errors);
6936             this.failureType = Roo.form.Action.SERVER_INVALID;
6937         }
6938         this.form.afterAction(this, false);
6939     },
6940     failure : function(response)
6941     {
6942         this.uploadComplete= true;
6943         if (this.haveProgress) {
6944             Roo.MessageBox.hide();
6945         }
6946         
6947         this.response = response;
6948         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6949         this.form.afterAction(this, false);
6950     },
6951     
6952     handleResponse : function(response){
6953         if(this.form.errorReader){
6954             var rs = this.form.errorReader.read(response);
6955             var errors = [];
6956             if(rs.records){
6957                 for(var i = 0, len = rs.records.length; i < len; i++) {
6958                     var r = rs.records[i];
6959                     errors[i] = r.data;
6960                 }
6961             }
6962             if(errors.length < 1){
6963                 errors = null;
6964             }
6965             return {
6966                 success : rs.success,
6967                 errors : errors
6968             };
6969         }
6970         var ret = false;
6971         try {
6972             ret = Roo.decode(response.responseText);
6973         } catch (e) {
6974             ret = {
6975                 success: false,
6976                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6977                 errors : []
6978             };
6979         }
6980         return ret;
6981         
6982     }
6983 });
6984
6985
6986 Roo.form.Action.Load = function(form, options){
6987     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6988     this.reader = this.form.reader;
6989 };
6990
6991 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6992     type : 'load',
6993
6994     run : function(){
6995         
6996         Roo.Ajax.request(Roo.apply(
6997                 this.createCallback(), {
6998                     method:this.getMethod(),
6999                     url:this.getUrl(false),
7000                     params:this.getParams()
7001         }));
7002     },
7003
7004     success : function(response){
7005         
7006         var result = this.processResponse(response);
7007         if(result === true || !result.success || !result.data){
7008             this.failureType = Roo.form.Action.LOAD_FAILURE;
7009             this.form.afterAction(this, false);
7010             return;
7011         }
7012         this.form.clearInvalid();
7013         this.form.setValues(result.data);
7014         this.form.afterAction(this, true);
7015     },
7016
7017     handleResponse : function(response){
7018         if(this.form.reader){
7019             var rs = this.form.reader.read(response);
7020             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7021             return {
7022                 success : rs.success,
7023                 data : data
7024             };
7025         }
7026         return Roo.decode(response.responseText);
7027     }
7028 });
7029
7030 Roo.form.Action.ACTION_TYPES = {
7031     'load' : Roo.form.Action.Load,
7032     'submit' : Roo.form.Action.Submit
7033 };/*
7034  * - LGPL
7035  *
7036  * form
7037  * 
7038  */
7039
7040 /**
7041  * @class Roo.bootstrap.Form
7042  * @extends Roo.bootstrap.Component
7043  * Bootstrap Form class
7044  * @cfg {String} method  GET | POST (default POST)
7045  * @cfg {String} labelAlign top | left (default top)
7046  * @cfg {String} align left  | right - for navbars
7047  * @cfg {Boolean} loadMask load mask when submit (default true)
7048
7049  * 
7050  * @constructor
7051  * Create a new Form
7052  * @param {Object} config The config object
7053  */
7054
7055
7056 Roo.bootstrap.Form = function(config){
7057     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7058     this.addEvents({
7059         /**
7060          * @event clientvalidation
7061          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7062          * @param {Form} this
7063          * @param {Boolean} valid true if the form has passed client-side validation
7064          */
7065         clientvalidation: true,
7066         /**
7067          * @event beforeaction
7068          * Fires before any action is performed. Return false to cancel the action.
7069          * @param {Form} this
7070          * @param {Action} action The action to be performed
7071          */
7072         beforeaction: true,
7073         /**
7074          * @event actionfailed
7075          * Fires when an action fails.
7076          * @param {Form} this
7077          * @param {Action} action The action that failed
7078          */
7079         actionfailed : true,
7080         /**
7081          * @event actioncomplete
7082          * Fires when an action is completed.
7083          * @param {Form} this
7084          * @param {Action} action The action that completed
7085          */
7086         actioncomplete : true
7087     });
7088     
7089 };
7090
7091 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7092       
7093      /**
7094      * @cfg {String} method
7095      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7096      */
7097     method : 'POST',
7098     /**
7099      * @cfg {String} url
7100      * The URL to use for form actions if one isn't supplied in the action options.
7101      */
7102     /**
7103      * @cfg {Boolean} fileUpload
7104      * Set to true if this form is a file upload.
7105      */
7106      
7107     /**
7108      * @cfg {Object} baseParams
7109      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7110      */
7111       
7112     /**
7113      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7114      */
7115     timeout: 30,
7116     /**
7117      * @cfg {Sting} align (left|right) for navbar forms
7118      */
7119     align : 'left',
7120
7121     // private
7122     activeAction : null,
7123  
7124     /**
7125      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7126      * element by passing it or its id or mask the form itself by passing in true.
7127      * @type Mixed
7128      */
7129     waitMsgTarget : false,
7130     
7131     loadMask : true,
7132     
7133     getAutoCreate : function(){
7134         
7135         var cfg = {
7136             tag: 'form',
7137             method : this.method || 'POST',
7138             id : this.id || Roo.id(),
7139             cls : ''
7140         };
7141         if (this.parent().xtype.match(/^Nav/)) {
7142             cfg.cls = 'navbar-form navbar-' + this.align;
7143             
7144         }
7145         
7146         if (this.labelAlign == 'left' ) {
7147             cfg.cls += ' form-horizontal';
7148         }
7149         
7150         
7151         return cfg;
7152     },
7153     initEvents : function()
7154     {
7155         this.el.on('submit', this.onSubmit, this);
7156         // this was added as random key presses on the form where triggering form submit.
7157         this.el.on('keypress', function(e) {
7158             if (e.getCharCode() != 13) {
7159                 return true;
7160             }
7161             // we might need to allow it for textareas.. and some other items.
7162             // check e.getTarget().
7163             
7164             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7165                 return true;
7166             }
7167         
7168             Roo.log("keypress blocked");
7169             
7170             e.preventDefault();
7171             return false;
7172         });
7173         
7174     },
7175     // private
7176     onSubmit : function(e){
7177         e.stopEvent();
7178     },
7179     
7180      /**
7181      * Returns true if client-side validation on the form is successful.
7182      * @return Boolean
7183      */
7184     isValid : function(){
7185         var items = this.getItems();
7186         var valid = true;
7187         items.each(function(f){
7188            if(!f.validate()){
7189                valid = false;
7190                
7191            }
7192         });
7193         return valid;
7194     },
7195     /**
7196      * Returns true if any fields in this form have changed since their original load.
7197      * @return Boolean
7198      */
7199     isDirty : function(){
7200         var dirty = false;
7201         var items = this.getItems();
7202         items.each(function(f){
7203            if(f.isDirty()){
7204                dirty = true;
7205                return false;
7206            }
7207            return true;
7208         });
7209         return dirty;
7210     },
7211      /**
7212      * Performs a predefined action (submit or load) or custom actions you define on this form.
7213      * @param {String} actionName The name of the action type
7214      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7215      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7216      * accept other config options):
7217      * <pre>
7218 Property          Type             Description
7219 ----------------  ---------------  ----------------------------------------------------------------------------------
7220 url               String           The url for the action (defaults to the form's url)
7221 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7222 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7223 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7224                                    validate the form on the client (defaults to false)
7225      * </pre>
7226      * @return {BasicForm} this
7227      */
7228     doAction : function(action, options){
7229         if(typeof action == 'string'){
7230             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7231         }
7232         if(this.fireEvent('beforeaction', this, action) !== false){
7233             this.beforeAction(action);
7234             action.run.defer(100, action);
7235         }
7236         return this;
7237     },
7238     
7239     // private
7240     beforeAction : function(action){
7241         var o = action.options;
7242         
7243         if(this.loadMask){
7244             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7245         }
7246         // not really supported yet.. ??
7247         
7248         //if(this.waitMsgTarget === true){
7249         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7250         //}else if(this.waitMsgTarget){
7251         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7252         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7253         //}else {
7254         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7255        // }
7256          
7257     },
7258
7259     // private
7260     afterAction : function(action, success){
7261         this.activeAction = null;
7262         var o = action.options;
7263         
7264         //if(this.waitMsgTarget === true){
7265             this.el.unmask();
7266         //}else if(this.waitMsgTarget){
7267         //    this.waitMsgTarget.unmask();
7268         //}else{
7269         //    Roo.MessageBox.updateProgress(1);
7270         //    Roo.MessageBox.hide();
7271        // }
7272         // 
7273         if(success){
7274             if(o.reset){
7275                 this.reset();
7276             }
7277             Roo.callback(o.success, o.scope, [this, action]);
7278             this.fireEvent('actioncomplete', this, action);
7279             
7280         }else{
7281             
7282             // failure condition..
7283             // we have a scenario where updates need confirming.
7284             // eg. if a locking scenario exists..
7285             // we look for { errors : { needs_confirm : true }} in the response.
7286             if (
7287                 (typeof(action.result) != 'undefined')  &&
7288                 (typeof(action.result.errors) != 'undefined')  &&
7289                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7290            ){
7291                 var _t = this;
7292                 Roo.log("not supported yet");
7293                  /*
7294                 
7295                 Roo.MessageBox.confirm(
7296                     "Change requires confirmation",
7297                     action.result.errorMsg,
7298                     function(r) {
7299                         if (r != 'yes') {
7300                             return;
7301                         }
7302                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7303                     }
7304                     
7305                 );
7306                 */
7307                 
7308                 
7309                 return;
7310             }
7311             
7312             Roo.callback(o.failure, o.scope, [this, action]);
7313             // show an error message if no failed handler is set..
7314             if (!this.hasListener('actionfailed')) {
7315                 Roo.log("need to add dialog support");
7316                 /*
7317                 Roo.MessageBox.alert("Error",
7318                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7319                         action.result.errorMsg :
7320                         "Saving Failed, please check your entries or try again"
7321                 );
7322                 */
7323             }
7324             
7325             this.fireEvent('actionfailed', this, action);
7326         }
7327         
7328     },
7329     /**
7330      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7331      * @param {String} id The value to search for
7332      * @return Field
7333      */
7334     findField : function(id){
7335         var items = this.getItems();
7336         var field = items.get(id);
7337         if(!field){
7338              items.each(function(f){
7339                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7340                     field = f;
7341                     return false;
7342                 }
7343                 return true;
7344             });
7345         }
7346         return field || null;
7347     },
7348      /**
7349      * Mark fields in this form invalid in bulk.
7350      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7351      * @return {BasicForm} this
7352      */
7353     markInvalid : function(errors){
7354         if(errors instanceof Array){
7355             for(var i = 0, len = errors.length; i < len; i++){
7356                 var fieldError = errors[i];
7357                 var f = this.findField(fieldError.id);
7358                 if(f){
7359                     f.markInvalid(fieldError.msg);
7360                 }
7361             }
7362         }else{
7363             var field, id;
7364             for(id in errors){
7365                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7366                     field.markInvalid(errors[id]);
7367                 }
7368             }
7369         }
7370         //Roo.each(this.childForms || [], function (f) {
7371         //    f.markInvalid(errors);
7372         //});
7373         
7374         return this;
7375     },
7376
7377     /**
7378      * Set values for fields in this form in bulk.
7379      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7380      * @return {BasicForm} this
7381      */
7382     setValues : function(values){
7383         if(values instanceof Array){ // array of objects
7384             for(var i = 0, len = values.length; i < len; i++){
7385                 var v = values[i];
7386                 var f = this.findField(v.id);
7387                 if(f){
7388                     f.setValue(v.value);
7389                     if(this.trackResetOnLoad){
7390                         f.originalValue = f.getValue();
7391                     }
7392                 }
7393             }
7394         }else{ // object hash
7395             var field, id;
7396             for(id in values){
7397                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7398                     
7399                     if (field.setFromData && 
7400                         field.valueField && 
7401                         field.displayField &&
7402                         // combos' with local stores can 
7403                         // be queried via setValue()
7404                         // to set their value..
7405                         (field.store && !field.store.isLocal)
7406                         ) {
7407                         // it's a combo
7408                         var sd = { };
7409                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7410                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7411                         field.setFromData(sd);
7412                         
7413                     } else {
7414                         field.setValue(values[id]);
7415                     }
7416                     
7417                     
7418                     if(this.trackResetOnLoad){
7419                         field.originalValue = field.getValue();
7420                     }
7421                 }
7422             }
7423         }
7424          
7425         //Roo.each(this.childForms || [], function (f) {
7426         //    f.setValues(values);
7427         //});
7428                 
7429         return this;
7430     },
7431
7432     /**
7433      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7434      * they are returned as an array.
7435      * @param {Boolean} asString
7436      * @return {Object}
7437      */
7438     getValues : function(asString){
7439         //if (this.childForms) {
7440             // copy values from the child forms
7441         //    Roo.each(this.childForms, function (f) {
7442         //        this.setValues(f.getValues());
7443         //    }, this);
7444         //}
7445         
7446         
7447         
7448         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7449         if(asString === true){
7450             return fs;
7451         }
7452         return Roo.urlDecode(fs);
7453     },
7454     
7455     /**
7456      * Returns the fields in this form as an object with key/value pairs. 
7457      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7458      * @return {Object}
7459      */
7460     getFieldValues : function(with_hidden)
7461     {
7462         var items = this.getItems();
7463         var ret = {};
7464         items.each(function(f){
7465             if (!f.getName()) {
7466                 return;
7467             }
7468             var v = f.getValue();
7469             if (f.inputType =='radio') {
7470                 if (typeof(ret[f.getName()]) == 'undefined') {
7471                     ret[f.getName()] = ''; // empty..
7472                 }
7473                 
7474                 if (!f.el.dom.checked) {
7475                     return;
7476                     
7477                 }
7478                 v = f.el.dom.value;
7479                 
7480             }
7481             
7482             // not sure if this supported any more..
7483             if ((typeof(v) == 'object') && f.getRawValue) {
7484                 v = f.getRawValue() ; // dates..
7485             }
7486             // combo boxes where name != hiddenName...
7487             if (f.name != f.getName()) {
7488                 ret[f.name] = f.getRawValue();
7489             }
7490             ret[f.getName()] = v;
7491         });
7492         
7493         return ret;
7494     },
7495
7496     /**
7497      * Clears all invalid messages in this form.
7498      * @return {BasicForm} this
7499      */
7500     clearInvalid : function(){
7501         var items = this.getItems();
7502         
7503         items.each(function(f){
7504            f.clearInvalid();
7505         });
7506         
7507         
7508         
7509         return this;
7510     },
7511
7512     /**
7513      * Resets this form.
7514      * @return {BasicForm} this
7515      */
7516     reset : function(){
7517         var items = this.getItems();
7518         items.each(function(f){
7519             f.reset();
7520         });
7521         
7522         Roo.each(this.childForms || [], function (f) {
7523             f.reset();
7524         });
7525        
7526         
7527         return this;
7528     },
7529     getItems : function()
7530     {
7531         var r=new Roo.util.MixedCollection(false, function(o){
7532             return o.id || (o.id = Roo.id());
7533         });
7534         var iter = function(el) {
7535             if (el.inputEl) {
7536                 r.add(el);
7537             }
7538             if (!el.items) {
7539                 return;
7540             }
7541             Roo.each(el.items,function(e) {
7542                 iter(e);
7543             });
7544             
7545             
7546         };
7547         
7548         iter(this);
7549         return r;
7550         
7551         
7552         
7553         
7554     }
7555     
7556 });
7557
7558  
7559 /*
7560  * Based on:
7561  * Ext JS Library 1.1.1
7562  * Copyright(c) 2006-2007, Ext JS, LLC.
7563  *
7564  * Originally Released Under LGPL - original licence link has changed is not relivant.
7565  *
7566  * Fork - LGPL
7567  * <script type="text/javascript">
7568  */
7569 /**
7570  * @class Roo.form.VTypes
7571  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7572  * @singleton
7573  */
7574 Roo.form.VTypes = function(){
7575     // closure these in so they are only created once.
7576     var alpha = /^[a-zA-Z_]+$/;
7577     var alphanum = /^[a-zA-Z0-9_]+$/;
7578     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7579     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7580
7581     // All these messages and functions are configurable
7582     return {
7583         /**
7584          * The function used to validate email addresses
7585          * @param {String} value The email address
7586          */
7587         'email' : function(v){
7588             return email.test(v);
7589         },
7590         /**
7591          * The error text to display when the email validation function returns false
7592          * @type String
7593          */
7594         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7595         /**
7596          * The keystroke filter mask to be applied on email input
7597          * @type RegExp
7598          */
7599         'emailMask' : /[a-z0-9_\.\-@]/i,
7600
7601         /**
7602          * The function used to validate URLs
7603          * @param {String} value The URL
7604          */
7605         'url' : function(v){
7606             return url.test(v);
7607         },
7608         /**
7609          * The error text to display when the url validation function returns false
7610          * @type String
7611          */
7612         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7613         
7614         /**
7615          * The function used to validate alpha values
7616          * @param {String} value The value
7617          */
7618         'alpha' : function(v){
7619             return alpha.test(v);
7620         },
7621         /**
7622          * The error text to display when the alpha validation function returns false
7623          * @type String
7624          */
7625         'alphaText' : 'This field should only contain letters and _',
7626         /**
7627          * The keystroke filter mask to be applied on alpha input
7628          * @type RegExp
7629          */
7630         'alphaMask' : /[a-z_]/i,
7631
7632         /**
7633          * The function used to validate alphanumeric values
7634          * @param {String} value The value
7635          */
7636         'alphanum' : function(v){
7637             return alphanum.test(v);
7638         },
7639         /**
7640          * The error text to display when the alphanumeric validation function returns false
7641          * @type String
7642          */
7643         'alphanumText' : 'This field should only contain letters, numbers and _',
7644         /**
7645          * The keystroke filter mask to be applied on alphanumeric input
7646          * @type RegExp
7647          */
7648         'alphanumMask' : /[a-z0-9_]/i
7649     };
7650 }();/*
7651  * - LGPL
7652  *
7653  * Input
7654  * 
7655  */
7656
7657 /**
7658  * @class Roo.bootstrap.Input
7659  * @extends Roo.bootstrap.Component
7660  * Bootstrap Input class
7661  * @cfg {Boolean} disabled is it disabled
7662  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7663  * @cfg {String} name name of the input
7664  * @cfg {string} fieldLabel - the label associated
7665  * @cfg {string} placeholder - placeholder to put in text.
7666  * @cfg {string}  before - input group add on before
7667  * @cfg {string} after - input group add on after
7668  * @cfg {string} size - (lg|sm) or leave empty..
7669  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7670  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7671  * @cfg {Number} md colspan out of 12 for computer-sized screens
7672  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7673  * @cfg {string} value default value of the input
7674  * @cfg {Number} labelWidth set the width of label (0-12)
7675  * @cfg {String} labelAlign (top|left)
7676  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7677  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7678
7679  * @cfg {String} align (left|center|right) Default left
7680  * @cfg {Boolean} forceFeedback (true|false) Default false
7681  * 
7682  * 
7683  * 
7684  * 
7685  * @constructor
7686  * Create a new Input
7687  * @param {Object} config The config object
7688  */
7689
7690 Roo.bootstrap.Input = function(config){
7691     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7692    
7693         this.addEvents({
7694             /**
7695              * @event focus
7696              * Fires when this field receives input focus.
7697              * @param {Roo.form.Field} this
7698              */
7699             focus : true,
7700             /**
7701              * @event blur
7702              * Fires when this field loses input focus.
7703              * @param {Roo.form.Field} this
7704              */
7705             blur : true,
7706             /**
7707              * @event specialkey
7708              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7709              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7710              * @param {Roo.form.Field} this
7711              * @param {Roo.EventObject} e The event object
7712              */
7713             specialkey : true,
7714             /**
7715              * @event change
7716              * Fires just before the field blurs if the field value has changed.
7717              * @param {Roo.form.Field} this
7718              * @param {Mixed} newValue The new value
7719              * @param {Mixed} oldValue The original value
7720              */
7721             change : true,
7722             /**
7723              * @event invalid
7724              * Fires after the field has been marked as invalid.
7725              * @param {Roo.form.Field} this
7726              * @param {String} msg The validation message
7727              */
7728             invalid : true,
7729             /**
7730              * @event valid
7731              * Fires after the field has been validated with no errors.
7732              * @param {Roo.form.Field} this
7733              */
7734             valid : true,
7735              /**
7736              * @event keyup
7737              * Fires after the key up
7738              * @param {Roo.form.Field} this
7739              * @param {Roo.EventObject}  e The event Object
7740              */
7741             keyup : true
7742         });
7743 };
7744
7745 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7746      /**
7747      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7748       automatic validation (defaults to "keyup").
7749      */
7750     validationEvent : "keyup",
7751      /**
7752      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7753      */
7754     validateOnBlur : true,
7755     /**
7756      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7757      */
7758     validationDelay : 250,
7759      /**
7760      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7761      */
7762     focusClass : "x-form-focus",  // not needed???
7763     
7764        
7765     /**
7766      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7767      */
7768     invalidClass : "has-warning",
7769     
7770     /**
7771      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7772      */
7773     validClass : "has-success",
7774     
7775     /**
7776      * @cfg {Boolean} hasFeedback (true|false) default true
7777      */
7778     hasFeedback : true,
7779     
7780     /**
7781      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7782      */
7783     invalidFeedbackClass : "glyphicon-warning-sign",
7784     
7785     /**
7786      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7787      */
7788     validFeedbackClass : "glyphicon-ok",
7789     
7790     /**
7791      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7792      */
7793     selectOnFocus : false,
7794     
7795      /**
7796      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7797      */
7798     maskRe : null,
7799        /**
7800      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7801      */
7802     vtype : null,
7803     
7804       /**
7805      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7806      */
7807     disableKeyFilter : false,
7808     
7809        /**
7810      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7811      */
7812     disabled : false,
7813      /**
7814      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7815      */
7816     allowBlank : true,
7817     /**
7818      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7819      */
7820     blankText : "This field is required",
7821     
7822      /**
7823      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7824      */
7825     minLength : 0,
7826     /**
7827      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7828      */
7829     maxLength : Number.MAX_VALUE,
7830     /**
7831      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7832      */
7833     minLengthText : "The minimum length for this field is {0}",
7834     /**
7835      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7836      */
7837     maxLengthText : "The maximum length for this field is {0}",
7838   
7839     
7840     /**
7841      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7842      * If available, this function will be called only after the basic validators all return true, and will be passed the
7843      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7844      */
7845     validator : null,
7846     /**
7847      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7848      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7849      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7850      */
7851     regex : null,
7852     /**
7853      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7854      */
7855     regexText : "",
7856     
7857     autocomplete: false,
7858     
7859     
7860     fieldLabel : '',
7861     inputType : 'text',
7862     
7863     name : false,
7864     placeholder: false,
7865     before : false,
7866     after : false,
7867     size : false,
7868     hasFocus : false,
7869     preventMark: false,
7870     isFormField : true,
7871     value : '',
7872     labelWidth : 2,
7873     labelAlign : false,
7874     readOnly : false,
7875     align : false,
7876     formatedValue : false,
7877     forceFeedback : false,
7878     
7879     parentLabelAlign : function()
7880     {
7881         var parent = this;
7882         while (parent.parent()) {
7883             parent = parent.parent();
7884             if (typeof(parent.labelAlign) !='undefined') {
7885                 return parent.labelAlign;
7886             }
7887         }
7888         return 'left';
7889         
7890     },
7891     
7892     getAutoCreate : function(){
7893         
7894         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7895         
7896         var id = Roo.id();
7897         
7898         var cfg = {};
7899         
7900         if(this.inputType != 'hidden'){
7901             cfg.cls = 'form-group' //input-group
7902         }
7903         
7904         var input =  {
7905             tag: 'input',
7906             id : id,
7907             type : this.inputType,
7908             value : this.value,
7909             cls : 'form-control',
7910             placeholder : this.placeholder || '',
7911             autocomplete : this.autocomplete || 'new-password'
7912         };
7913         
7914         
7915         if(this.align){
7916             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7917         }
7918         
7919         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7920             input.maxLength = this.maxLength;
7921         }
7922         
7923         if (this.disabled) {
7924             input.disabled=true;
7925         }
7926         
7927         if (this.readOnly) {
7928             input.readonly=true;
7929         }
7930         
7931         if (this.name) {
7932             input.name = this.name;
7933         }
7934         if (this.size) {
7935             input.cls += ' input-' + this.size;
7936         }
7937         var settings=this;
7938         ['xs','sm','md','lg'].map(function(size){
7939             if (settings[size]) {
7940                 cfg.cls += ' col-' + size + '-' + settings[size];
7941             }
7942         });
7943         
7944         var inputblock = input;
7945         
7946         var feedback = {
7947             tag: 'span',
7948             cls: 'glyphicon form-control-feedback'
7949         };
7950             
7951         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7952             
7953             inputblock = {
7954                 cls : 'has-feedback',
7955                 cn :  [
7956                     input,
7957                     feedback
7958                 ] 
7959             };  
7960         }
7961         
7962         if (this.before || this.after) {
7963             
7964             inputblock = {
7965                 cls : 'input-group',
7966                 cn :  [] 
7967             };
7968             
7969             if (this.before && typeof(this.before) == 'string') {
7970                 
7971                 inputblock.cn.push({
7972                     tag :'span',
7973                     cls : 'roo-input-before input-group-addon',
7974                     html : this.before
7975                 });
7976             }
7977             if (this.before && typeof(this.before) == 'object') {
7978                 this.before = Roo.factory(this.before);
7979                 
7980                 inputblock.cn.push({
7981                     tag :'span',
7982                     cls : 'roo-input-before input-group-' +
7983                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7984                 });
7985             }
7986             
7987             inputblock.cn.push(input);
7988             
7989             if (this.after && typeof(this.after) == 'string') {
7990                 inputblock.cn.push({
7991                     tag :'span',
7992                     cls : 'roo-input-after input-group-addon',
7993                     html : this.after
7994                 });
7995             }
7996             if (this.after && typeof(this.after) == 'object') {
7997                 this.after = Roo.factory(this.after);
7998                 
7999                 inputblock.cn.push({
8000                     tag :'span',
8001                     cls : 'roo-input-after input-group-' +
8002                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8003                 });
8004             }
8005             
8006             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8007                 inputblock.cls += ' has-feedback';
8008                 inputblock.cn.push(feedback);
8009             }
8010         };
8011         
8012         if (align ==='left' && this.fieldLabel.length) {
8013                 
8014                 cfg.cn = [
8015                     
8016                     {
8017                         tag: 'label',
8018                         'for' :  id,
8019                         cls : 'control-label col-sm-' + this.labelWidth,
8020                         html : this.fieldLabel
8021                         
8022                     },
8023                     {
8024                         cls : "col-sm-" + (12 - this.labelWidth), 
8025                         cn: [
8026                             inputblock
8027                         ]
8028                     }
8029                     
8030                 ];
8031         } else if ( this.fieldLabel.length) {
8032                 
8033                  cfg.cn = [
8034                    
8035                     {
8036                         tag: 'label',
8037                         //cls : 'input-group-addon',
8038                         html : this.fieldLabel
8039                         
8040                     },
8041                     
8042                     inputblock
8043                     
8044                 ];
8045
8046         } else {
8047             
8048                 cfg.cn = [
8049                     
8050                         inputblock
8051                     
8052                 ];
8053                 
8054                 
8055         };
8056         
8057         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8058            cfg.cls += ' navbar-form';
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         
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.originalValue = this.getValue();
13580         
13581         this.inputEl().on("click", this.showTouchView, this);
13582         
13583         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13584         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13585         
13586         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13587         
13588         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13589         this.store.on('load', this.onTouchViewLoad, this);
13590         this.store.on('loadexception', this.onTouchViewLoadException, this);
13591         
13592         if(this.hiddenName){
13593             
13594             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13595             
13596             this.hiddenField.dom.value =
13597                 this.hiddenValue !== undefined ? this.hiddenValue :
13598                 this.value !== undefined ? this.value : '';
13599         
13600             this.el.dom.removeAttribute('name');
13601             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13602         }
13603         
13604         if(this.multiple){
13605             this.choices = this.el.select('ul.select2-choices', true).first();
13606             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13607         }
13608         
13609         if(this.removable && !this.multiple){
13610             var close = this.closeTriggerEl();
13611             if(close){
13612                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13613                 close.on('click', this.removeBtnClick, this, close);
13614             }
13615         }
13616         /*
13617          * fix the bug in Safari iOS8
13618          */
13619         this.inputEl().on("focus", function(e){
13620             document.activeElement.blur();
13621         }, this);
13622         
13623         return;
13624         
13625         
13626     },
13627     
13628     renderTouchView : function()
13629     {
13630         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13631         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13632         
13633         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13634         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13635         
13636         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13637         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13638         this.touchViewBodyEl.setStyle('overflow', 'auto');
13639         
13640         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13641         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13642         
13643         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13644         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13645         
13646     },
13647     
13648     showTouchView : function()
13649     {
13650         if(this.disabled){
13651             return;
13652         }
13653         
13654         this.touchViewHeaderEl.hide();
13655
13656         if(this.fieldLabel.length){
13657             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13658             this.touchViewHeaderEl.show();
13659         }
13660
13661         this.touchViewEl.show();
13662
13663         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13664         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13665
13666         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13667
13668         if(this.fieldLabel.length){
13669             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13670         }
13671         
13672         this.touchViewBodyEl.setHeight(bodyHeight);
13673
13674         if(this.animate){
13675             var _this = this;
13676             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13677         }else{
13678             this.touchViewEl.addClass('in');
13679         }
13680
13681         this.doTouchViewQuery();
13682         
13683     },
13684     
13685     hideTouchView : function()
13686     {
13687         this.touchViewEl.removeClass('in');
13688
13689         if(this.animate){
13690             var _this = this;
13691             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13692         }else{
13693             this.touchViewEl.setStyle('display', 'none');
13694         }
13695         
13696     },
13697     
13698     setTouchViewValue : function()
13699     {
13700         if(this.multiple){
13701             this.clearItem();
13702         
13703             var _this = this;
13704
13705             Roo.each(this.tickItems, function(o){
13706                 this.addItem(o);
13707             }, this);
13708         }
13709         
13710         this.hideTouchView();
13711     },
13712     
13713     doTouchViewQuery : function()
13714     {
13715         var qe = {
13716             query: '',
13717             forceAll: true,
13718             combo: this,
13719             cancel:false
13720         };
13721         
13722         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13723             return false;
13724         }
13725         
13726         if(!this.alwaysQuery || this.mode == 'local'){
13727             this.onTouchViewLoad();
13728             return;
13729         }
13730         
13731         this.store.load();
13732     },
13733     
13734     onTouchViewBeforeLoad : function(combo,opts)
13735     {
13736         return;
13737     },
13738
13739     // private
13740     onTouchViewLoad : function()
13741     {
13742         if(this.store.getCount() < 1){
13743             this.onTouchViewEmptyResults();
13744             return;
13745         }
13746         
13747         this.clearTouchView();
13748         
13749         var rawValue = this.getRawValue();
13750         
13751         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13752         
13753         this.tickItems = [];
13754         
13755         this.store.data.each(function(d, rowIndex){
13756             var row = this.touchViewListGroup.createChild(template);
13757             
13758             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13759                 var cfg = {
13760                     data : d.data,
13761                     html : d.data[this.displayField]
13762                 };
13763                 
13764                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13765                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13766                 }
13767             }
13768             
13769             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13770                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13771             }
13772             
13773             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13774                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13775                 this.tickItems.push(d.data);
13776             }
13777             
13778             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13779             
13780         }, this);
13781         
13782         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13783         
13784         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13785
13786         if(this.fieldLabel.length){
13787             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13788         }
13789
13790         var listHeight = this.touchViewListGroup.getHeight();
13791         
13792         var _this = this;
13793         
13794         if(firstChecked && listHeight > bodyHeight){
13795             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13796         }
13797         
13798     },
13799     
13800     onTouchViewLoadException : function()
13801     {
13802         this.hideTouchView();
13803     },
13804     
13805     onTouchViewEmptyResults : function()
13806     {
13807         this.clearTouchView();
13808         
13809         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13810         
13811         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13812         
13813     },
13814     
13815     clearTouchView : function()
13816     {
13817         this.touchViewListGroup.dom.innerHTML = '';
13818     },
13819     
13820     onTouchViewClick : function(e, el, o)
13821     {
13822         e.preventDefault();
13823         
13824         var row = o.row;
13825         var rowIndex = o.rowIndex;
13826         
13827         var r = this.store.getAt(rowIndex);
13828         
13829         if(!this.multiple){
13830             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13831                 c.dom.removeAttribute('checked');
13832             }, this);
13833             
13834             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13835         
13836             this.setFromData(r.data);
13837             
13838             var close = this.closeTriggerEl();
13839         
13840             if(close){
13841                 close.show();
13842             }
13843
13844             this.hideTouchView();
13845             
13846             this.fireEvent('select', this, r, rowIndex);
13847             
13848             return;
13849         }
13850         
13851         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13852             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13853             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13854             return;
13855         }
13856         
13857         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13858         this.addItem(r.data);
13859         this.tickItems.push(r.data);
13860         
13861     }
13862     
13863
13864     /** 
13865     * @cfg {Boolean} grow 
13866     * @hide 
13867     */
13868     /** 
13869     * @cfg {Number} growMin 
13870     * @hide 
13871     */
13872     /** 
13873     * @cfg {Number} growMax 
13874     * @hide 
13875     */
13876     /**
13877      * @hide
13878      * @method autoSize
13879      */
13880 });
13881
13882 Roo.apply(Roo.bootstrap.ComboBox,  {
13883     
13884     header : {
13885         tag: 'div',
13886         cls: 'modal-header',
13887         cn: [
13888             {
13889                 tag: 'h4',
13890                 cls: 'modal-title'
13891             }
13892         ]
13893     },
13894     
13895     body : {
13896         tag: 'div',
13897         cls: 'modal-body',
13898         cn: [
13899             {
13900                 tag: 'ul',
13901                 cls: 'list-group'
13902             }
13903         ]
13904     },
13905     
13906     listItemRadio : {
13907         tag: 'li',
13908         cls: 'list-group-item',
13909         cn: [
13910             {
13911                 tag: 'span',
13912                 cls: 'roo-combobox-list-group-item-value'
13913             },
13914             {
13915                 tag: 'div',
13916                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13917                 cn: [
13918                     {
13919                         tag: 'input',
13920                         type: 'radio'
13921                     },
13922                     {
13923                         tag: 'label'
13924                     }
13925                 ]
13926             }
13927         ]
13928     },
13929     
13930     listItemCheckbox : {
13931         tag: 'li',
13932         cls: 'list-group-item',
13933         cn: [
13934             {
13935                 tag: 'span',
13936                 cls: 'roo-combobox-list-group-item-value'
13937             },
13938             {
13939                 tag: 'div',
13940                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13941                 cn: [
13942                     {
13943                         tag: 'input',
13944                         type: 'checkbox'
13945                     },
13946                     {
13947                         tag: 'label'
13948                     }
13949                 ]
13950             }
13951         ]
13952     },
13953     
13954     emptyResult : {
13955         tag: 'div',
13956         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13957     },
13958     
13959     footer : {
13960         tag: 'div',
13961         cls: 'modal-footer',
13962         cn: [
13963             {
13964                 tag: 'div',
13965                 cls: 'row',
13966                 cn: [
13967                     {
13968                         tag: 'div',
13969                         cls: 'col-xs-6 text-left',
13970                         cn: {
13971                             tag: 'button',
13972                             cls: 'btn btn-danger roo-touch-view-cancel',
13973                             html: 'Cancel'
13974                         }
13975                     },
13976                     {
13977                         tag: 'div',
13978                         cls: 'col-xs-6 text-right',
13979                         cn: {
13980                             tag: 'button',
13981                             cls: 'btn btn-success roo-touch-view-ok',
13982                             html: 'OK'
13983                         }
13984                     }
13985                 ]
13986             }
13987         ]
13988         
13989     }
13990 });
13991
13992 Roo.apply(Roo.bootstrap.ComboBox,  {
13993     
13994     touchViewTemplate : {
13995         tag: 'div',
13996         cls: 'modal fade roo-combobox-touch-view',
13997         cn: [
13998             {
13999                 tag: 'div',
14000                 cls: 'modal-dialog',
14001                 style : 'position:fixed', // we have to fix position....
14002                 cn: [
14003                     {
14004                         tag: 'div',
14005                         cls: 'modal-content',
14006                         cn: [
14007                             Roo.bootstrap.ComboBox.header,
14008                             Roo.bootstrap.ComboBox.body,
14009                             Roo.bootstrap.ComboBox.footer
14010                         ]
14011                     }
14012                 ]
14013             }
14014         ]
14015     }
14016 });/*
14017  * Based on:
14018  * Ext JS Library 1.1.1
14019  * Copyright(c) 2006-2007, Ext JS, LLC.
14020  *
14021  * Originally Released Under LGPL - original licence link has changed is not relivant.
14022  *
14023  * Fork - LGPL
14024  * <script type="text/javascript">
14025  */
14026
14027 /**
14028  * @class Roo.View
14029  * @extends Roo.util.Observable
14030  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14031  * This class also supports single and multi selection modes. <br>
14032  * Create a data model bound view:
14033  <pre><code>
14034  var store = new Roo.data.Store(...);
14035
14036  var view = new Roo.View({
14037     el : "my-element",
14038     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14039  
14040     singleSelect: true,
14041     selectedClass: "ydataview-selected",
14042     store: store
14043  });
14044
14045  // listen for node click?
14046  view.on("click", function(vw, index, node, e){
14047  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14048  });
14049
14050  // load XML data
14051  dataModel.load("foobar.xml");
14052  </code></pre>
14053  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14054  * <br><br>
14055  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14056  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14057  * 
14058  * Note: old style constructor is still suported (container, template, config)
14059  * 
14060  * @constructor
14061  * Create a new View
14062  * @param {Object} config The config object
14063  * 
14064  */
14065 Roo.View = function(config, depreciated_tpl, depreciated_config){
14066     
14067     this.parent = false;
14068     
14069     if (typeof(depreciated_tpl) == 'undefined') {
14070         // new way.. - universal constructor.
14071         Roo.apply(this, config);
14072         this.el  = Roo.get(this.el);
14073     } else {
14074         // old format..
14075         this.el  = Roo.get(config);
14076         this.tpl = depreciated_tpl;
14077         Roo.apply(this, depreciated_config);
14078     }
14079     this.wrapEl  = this.el.wrap().wrap();
14080     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14081     
14082     
14083     if(typeof(this.tpl) == "string"){
14084         this.tpl = new Roo.Template(this.tpl);
14085     } else {
14086         // support xtype ctors..
14087         this.tpl = new Roo.factory(this.tpl, Roo);
14088     }
14089     
14090     
14091     this.tpl.compile();
14092     
14093     /** @private */
14094     this.addEvents({
14095         /**
14096          * @event beforeclick
14097          * Fires before a click is processed. Returns false to cancel the default action.
14098          * @param {Roo.View} this
14099          * @param {Number} index The index of the target node
14100          * @param {HTMLElement} node The target node
14101          * @param {Roo.EventObject} e The raw event object
14102          */
14103             "beforeclick" : true,
14104         /**
14105          * @event click
14106          * Fires when a template node is clicked.
14107          * @param {Roo.View} this
14108          * @param {Number} index The index of the target node
14109          * @param {HTMLElement} node The target node
14110          * @param {Roo.EventObject} e The raw event object
14111          */
14112             "click" : true,
14113         /**
14114          * @event dblclick
14115          * Fires when a template node is double clicked.
14116          * @param {Roo.View} this
14117          * @param {Number} index The index of the target node
14118          * @param {HTMLElement} node The target node
14119          * @param {Roo.EventObject} e The raw event object
14120          */
14121             "dblclick" : true,
14122         /**
14123          * @event contextmenu
14124          * Fires when a template node is right clicked.
14125          * @param {Roo.View} this
14126          * @param {Number} index The index of the target node
14127          * @param {HTMLElement} node The target node
14128          * @param {Roo.EventObject} e The raw event object
14129          */
14130             "contextmenu" : true,
14131         /**
14132          * @event selectionchange
14133          * Fires when the selected nodes change.
14134          * @param {Roo.View} this
14135          * @param {Array} selections Array of the selected nodes
14136          */
14137             "selectionchange" : true,
14138     
14139         /**
14140          * @event beforeselect
14141          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14142          * @param {Roo.View} this
14143          * @param {HTMLElement} node The node to be selected
14144          * @param {Array} selections Array of currently selected nodes
14145          */
14146             "beforeselect" : true,
14147         /**
14148          * @event preparedata
14149          * Fires on every row to render, to allow you to change the data.
14150          * @param {Roo.View} this
14151          * @param {Object} data to be rendered (change this)
14152          */
14153           "preparedata" : true
14154           
14155           
14156         });
14157
14158
14159
14160     this.el.on({
14161         "click": this.onClick,
14162         "dblclick": this.onDblClick,
14163         "contextmenu": this.onContextMenu,
14164         scope:this
14165     });
14166
14167     this.selections = [];
14168     this.nodes = [];
14169     this.cmp = new Roo.CompositeElementLite([]);
14170     if(this.store){
14171         this.store = Roo.factory(this.store, Roo.data);
14172         this.setStore(this.store, true);
14173     }
14174     
14175     if ( this.footer && this.footer.xtype) {
14176            
14177          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14178         
14179         this.footer.dataSource = this.store;
14180         this.footer.container = fctr;
14181         this.footer = Roo.factory(this.footer, Roo);
14182         fctr.insertFirst(this.el);
14183         
14184         // this is a bit insane - as the paging toolbar seems to detach the el..
14185 //        dom.parentNode.parentNode.parentNode
14186          // they get detached?
14187     }
14188     
14189     
14190     Roo.View.superclass.constructor.call(this);
14191     
14192     
14193 };
14194
14195 Roo.extend(Roo.View, Roo.util.Observable, {
14196     
14197      /**
14198      * @cfg {Roo.data.Store} store Data store to load data from.
14199      */
14200     store : false,
14201     
14202     /**
14203      * @cfg {String|Roo.Element} el The container element.
14204      */
14205     el : '',
14206     
14207     /**
14208      * @cfg {String|Roo.Template} tpl The template used by this View 
14209      */
14210     tpl : false,
14211     /**
14212      * @cfg {String} dataName the named area of the template to use as the data area
14213      *                          Works with domtemplates roo-name="name"
14214      */
14215     dataName: false,
14216     /**
14217      * @cfg {String} selectedClass The css class to add to selected nodes
14218      */
14219     selectedClass : "x-view-selected",
14220      /**
14221      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14222      */
14223     emptyText : "",
14224     
14225     /**
14226      * @cfg {String} text to display on mask (default Loading)
14227      */
14228     mask : false,
14229     /**
14230      * @cfg {Boolean} multiSelect Allow multiple selection
14231      */
14232     multiSelect : false,
14233     /**
14234      * @cfg {Boolean} singleSelect Allow single selection
14235      */
14236     singleSelect:  false,
14237     
14238     /**
14239      * @cfg {Boolean} toggleSelect - selecting 
14240      */
14241     toggleSelect : false,
14242     
14243     /**
14244      * @cfg {Boolean} tickable - selecting 
14245      */
14246     tickable : false,
14247     
14248     /**
14249      * Returns the element this view is bound to.
14250      * @return {Roo.Element}
14251      */
14252     getEl : function(){
14253         return this.wrapEl;
14254     },
14255     
14256     
14257
14258     /**
14259      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14260      */
14261     refresh : function(){
14262         //Roo.log('refresh');
14263         var t = this.tpl;
14264         
14265         // if we are using something like 'domtemplate', then
14266         // the what gets used is:
14267         // t.applySubtemplate(NAME, data, wrapping data..)
14268         // the outer template then get' applied with
14269         //     the store 'extra data'
14270         // and the body get's added to the
14271         //      roo-name="data" node?
14272         //      <span class='roo-tpl-{name}'></span> ?????
14273         
14274         
14275         
14276         this.clearSelections();
14277         this.el.update("");
14278         var html = [];
14279         var records = this.store.getRange();
14280         if(records.length < 1) {
14281             
14282             // is this valid??  = should it render a template??
14283             
14284             this.el.update(this.emptyText);
14285             return;
14286         }
14287         var el = this.el;
14288         if (this.dataName) {
14289             this.el.update(t.apply(this.store.meta)); //????
14290             el = this.el.child('.roo-tpl-' + this.dataName);
14291         }
14292         
14293         for(var i = 0, len = records.length; i < len; i++){
14294             var data = this.prepareData(records[i].data, i, records[i]);
14295             this.fireEvent("preparedata", this, data, i, records[i]);
14296             
14297             var d = Roo.apply({}, data);
14298             
14299             if(this.tickable){
14300                 Roo.apply(d, {'roo-id' : Roo.id()});
14301                 
14302                 var _this = this;
14303             
14304                 Roo.each(this.parent.item, function(item){
14305                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14306                         return;
14307                     }
14308                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14309                 });
14310             }
14311             
14312             html[html.length] = Roo.util.Format.trim(
14313                 this.dataName ?
14314                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14315                     t.apply(d)
14316             );
14317         }
14318         
14319         
14320         
14321         el.update(html.join(""));
14322         this.nodes = el.dom.childNodes;
14323         this.updateIndexes(0);
14324     },
14325     
14326
14327     /**
14328      * Function to override to reformat the data that is sent to
14329      * the template for each node.
14330      * DEPRICATED - use the preparedata event handler.
14331      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14332      * a JSON object for an UpdateManager bound view).
14333      */
14334     prepareData : function(data, index, record)
14335     {
14336         this.fireEvent("preparedata", this, data, index, record);
14337         return data;
14338     },
14339
14340     onUpdate : function(ds, record){
14341         // Roo.log('on update');   
14342         this.clearSelections();
14343         var index = this.store.indexOf(record);
14344         var n = this.nodes[index];
14345         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14346         n.parentNode.removeChild(n);
14347         this.updateIndexes(index, index);
14348     },
14349
14350     
14351     
14352 // --------- FIXME     
14353     onAdd : function(ds, records, index)
14354     {
14355         //Roo.log(['on Add', ds, records, index] );        
14356         this.clearSelections();
14357         if(this.nodes.length == 0){
14358             this.refresh();
14359             return;
14360         }
14361         var n = this.nodes[index];
14362         for(var i = 0, len = records.length; i < len; i++){
14363             var d = this.prepareData(records[i].data, i, records[i]);
14364             if(n){
14365                 this.tpl.insertBefore(n, d);
14366             }else{
14367                 
14368                 this.tpl.append(this.el, d);
14369             }
14370         }
14371         this.updateIndexes(index);
14372     },
14373
14374     onRemove : function(ds, record, index){
14375        // Roo.log('onRemove');
14376         this.clearSelections();
14377         var el = this.dataName  ?
14378             this.el.child('.roo-tpl-' + this.dataName) :
14379             this.el; 
14380         
14381         el.dom.removeChild(this.nodes[index]);
14382         this.updateIndexes(index);
14383     },
14384
14385     /**
14386      * Refresh an individual node.
14387      * @param {Number} index
14388      */
14389     refreshNode : function(index){
14390         this.onUpdate(this.store, this.store.getAt(index));
14391     },
14392
14393     updateIndexes : function(startIndex, endIndex){
14394         var ns = this.nodes;
14395         startIndex = startIndex || 0;
14396         endIndex = endIndex || ns.length - 1;
14397         for(var i = startIndex; i <= endIndex; i++){
14398             ns[i].nodeIndex = i;
14399         }
14400     },
14401
14402     /**
14403      * Changes the data store this view uses and refresh the view.
14404      * @param {Store} store
14405      */
14406     setStore : function(store, initial){
14407         if(!initial && this.store){
14408             this.store.un("datachanged", this.refresh);
14409             this.store.un("add", this.onAdd);
14410             this.store.un("remove", this.onRemove);
14411             this.store.un("update", this.onUpdate);
14412             this.store.un("clear", this.refresh);
14413             this.store.un("beforeload", this.onBeforeLoad);
14414             this.store.un("load", this.onLoad);
14415             this.store.un("loadexception", this.onLoad);
14416         }
14417         if(store){
14418           
14419             store.on("datachanged", this.refresh, this);
14420             store.on("add", this.onAdd, this);
14421             store.on("remove", this.onRemove, this);
14422             store.on("update", this.onUpdate, this);
14423             store.on("clear", this.refresh, this);
14424             store.on("beforeload", this.onBeforeLoad, this);
14425             store.on("load", this.onLoad, this);
14426             store.on("loadexception", this.onLoad, this);
14427         }
14428         
14429         if(store){
14430             this.refresh();
14431         }
14432     },
14433     /**
14434      * onbeforeLoad - masks the loading area.
14435      *
14436      */
14437     onBeforeLoad : function(store,opts)
14438     {
14439          //Roo.log('onBeforeLoad');   
14440         if (!opts.add) {
14441             this.el.update("");
14442         }
14443         this.el.mask(this.mask ? this.mask : "Loading" ); 
14444     },
14445     onLoad : function ()
14446     {
14447         this.el.unmask();
14448     },
14449     
14450
14451     /**
14452      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14453      * @param {HTMLElement} node
14454      * @return {HTMLElement} The template node
14455      */
14456     findItemFromChild : function(node){
14457         var el = this.dataName  ?
14458             this.el.child('.roo-tpl-' + this.dataName,true) :
14459             this.el.dom; 
14460         
14461         if(!node || node.parentNode == el){
14462                     return node;
14463             }
14464             var p = node.parentNode;
14465             while(p && p != el){
14466             if(p.parentNode == el){
14467                 return p;
14468             }
14469             p = p.parentNode;
14470         }
14471             return null;
14472     },
14473
14474     /** @ignore */
14475     onClick : function(e){
14476         var item = this.findItemFromChild(e.getTarget());
14477         if(item){
14478             var index = this.indexOf(item);
14479             if(this.onItemClick(item, index, e) !== false){
14480                 this.fireEvent("click", this, index, item, e);
14481             }
14482         }else{
14483             this.clearSelections();
14484         }
14485     },
14486
14487     /** @ignore */
14488     onContextMenu : function(e){
14489         var item = this.findItemFromChild(e.getTarget());
14490         if(item){
14491             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14492         }
14493     },
14494
14495     /** @ignore */
14496     onDblClick : function(e){
14497         var item = this.findItemFromChild(e.getTarget());
14498         if(item){
14499             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14500         }
14501     },
14502
14503     onItemClick : function(item, index, e)
14504     {
14505         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14506             return false;
14507         }
14508         if (this.toggleSelect) {
14509             var m = this.isSelected(item) ? 'unselect' : 'select';
14510             //Roo.log(m);
14511             var _t = this;
14512             _t[m](item, true, false);
14513             return true;
14514         }
14515         if(this.multiSelect || this.singleSelect){
14516             if(this.multiSelect && e.shiftKey && this.lastSelection){
14517                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14518             }else{
14519                 this.select(item, this.multiSelect && e.ctrlKey);
14520                 this.lastSelection = item;
14521             }
14522             
14523             if(!this.tickable){
14524                 e.preventDefault();
14525             }
14526             
14527         }
14528         return true;
14529     },
14530
14531     /**
14532      * Get the number of selected nodes.
14533      * @return {Number}
14534      */
14535     getSelectionCount : function(){
14536         return this.selections.length;
14537     },
14538
14539     /**
14540      * Get the currently selected nodes.
14541      * @return {Array} An array of HTMLElements
14542      */
14543     getSelectedNodes : function(){
14544         return this.selections;
14545     },
14546
14547     /**
14548      * Get the indexes of the selected nodes.
14549      * @return {Array}
14550      */
14551     getSelectedIndexes : function(){
14552         var indexes = [], s = this.selections;
14553         for(var i = 0, len = s.length; i < len; i++){
14554             indexes.push(s[i].nodeIndex);
14555         }
14556         return indexes;
14557     },
14558
14559     /**
14560      * Clear all selections
14561      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14562      */
14563     clearSelections : function(suppressEvent){
14564         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14565             this.cmp.elements = this.selections;
14566             this.cmp.removeClass(this.selectedClass);
14567             this.selections = [];
14568             if(!suppressEvent){
14569                 this.fireEvent("selectionchange", this, this.selections);
14570             }
14571         }
14572     },
14573
14574     /**
14575      * Returns true if the passed node is selected
14576      * @param {HTMLElement/Number} node The node or node index
14577      * @return {Boolean}
14578      */
14579     isSelected : function(node){
14580         var s = this.selections;
14581         if(s.length < 1){
14582             return false;
14583         }
14584         node = this.getNode(node);
14585         return s.indexOf(node) !== -1;
14586     },
14587
14588     /**
14589      * Selects nodes.
14590      * @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
14591      * @param {Boolean} keepExisting (optional) true to keep existing selections
14592      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14593      */
14594     select : function(nodeInfo, keepExisting, suppressEvent){
14595         if(nodeInfo instanceof Array){
14596             if(!keepExisting){
14597                 this.clearSelections(true);
14598             }
14599             for(var i = 0, len = nodeInfo.length; i < len; i++){
14600                 this.select(nodeInfo[i], true, true);
14601             }
14602             return;
14603         } 
14604         var node = this.getNode(nodeInfo);
14605         if(!node || this.isSelected(node)){
14606             return; // already selected.
14607         }
14608         if(!keepExisting){
14609             this.clearSelections(true);
14610         }
14611         
14612         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14613             Roo.fly(node).addClass(this.selectedClass);
14614             this.selections.push(node);
14615             if(!suppressEvent){
14616                 this.fireEvent("selectionchange", this, this.selections);
14617             }
14618         }
14619         
14620         
14621     },
14622       /**
14623      * Unselects nodes.
14624      * @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
14625      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14626      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14627      */
14628     unselect : function(nodeInfo, keepExisting, suppressEvent)
14629     {
14630         if(nodeInfo instanceof Array){
14631             Roo.each(this.selections, function(s) {
14632                 this.unselect(s, nodeInfo);
14633             }, this);
14634             return;
14635         }
14636         var node = this.getNode(nodeInfo);
14637         if(!node || !this.isSelected(node)){
14638             //Roo.log("not selected");
14639             return; // not selected.
14640         }
14641         // fireevent???
14642         var ns = [];
14643         Roo.each(this.selections, function(s) {
14644             if (s == node ) {
14645                 Roo.fly(node).removeClass(this.selectedClass);
14646
14647                 return;
14648             }
14649             ns.push(s);
14650         },this);
14651         
14652         this.selections= ns;
14653         this.fireEvent("selectionchange", this, this.selections);
14654     },
14655
14656     /**
14657      * Gets a template node.
14658      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14659      * @return {HTMLElement} The node or null if it wasn't found
14660      */
14661     getNode : function(nodeInfo){
14662         if(typeof nodeInfo == "string"){
14663             return document.getElementById(nodeInfo);
14664         }else if(typeof nodeInfo == "number"){
14665             return this.nodes[nodeInfo];
14666         }
14667         return nodeInfo;
14668     },
14669
14670     /**
14671      * Gets a range template nodes.
14672      * @param {Number} startIndex
14673      * @param {Number} endIndex
14674      * @return {Array} An array of nodes
14675      */
14676     getNodes : function(start, end){
14677         var ns = this.nodes;
14678         start = start || 0;
14679         end = typeof end == "undefined" ? ns.length - 1 : end;
14680         var nodes = [];
14681         if(start <= end){
14682             for(var i = start; i <= end; i++){
14683                 nodes.push(ns[i]);
14684             }
14685         } else{
14686             for(var i = start; i >= end; i--){
14687                 nodes.push(ns[i]);
14688             }
14689         }
14690         return nodes;
14691     },
14692
14693     /**
14694      * Finds the index of the passed node
14695      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14696      * @return {Number} The index of the node or -1
14697      */
14698     indexOf : function(node){
14699         node = this.getNode(node);
14700         if(typeof node.nodeIndex == "number"){
14701             return node.nodeIndex;
14702         }
14703         var ns = this.nodes;
14704         for(var i = 0, len = ns.length; i < len; i++){
14705             if(ns[i] == node){
14706                 return i;
14707             }
14708         }
14709         return -1;
14710     }
14711 });
14712 /*
14713  * - LGPL
14714  *
14715  * based on jquery fullcalendar
14716  * 
14717  */
14718
14719 Roo.bootstrap = Roo.bootstrap || {};
14720 /**
14721  * @class Roo.bootstrap.Calendar
14722  * @extends Roo.bootstrap.Component
14723  * Bootstrap Calendar class
14724  * @cfg {Boolean} loadMask (true|false) default false
14725  * @cfg {Object} header generate the user specific header of the calendar, default false
14726
14727  * @constructor
14728  * Create a new Container
14729  * @param {Object} config The config object
14730  */
14731
14732
14733
14734 Roo.bootstrap.Calendar = function(config){
14735     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14736      this.addEvents({
14737         /**
14738              * @event select
14739              * Fires when a date is selected
14740              * @param {DatePicker} this
14741              * @param {Date} date The selected date
14742              */
14743         'select': true,
14744         /**
14745              * @event monthchange
14746              * Fires when the displayed month changes 
14747              * @param {DatePicker} this
14748              * @param {Date} date The selected month
14749              */
14750         'monthchange': true,
14751         /**
14752              * @event evententer
14753              * Fires when mouse over an event
14754              * @param {Calendar} this
14755              * @param {event} Event
14756              */
14757         'evententer': true,
14758         /**
14759              * @event eventleave
14760              * Fires when the mouse leaves an
14761              * @param {Calendar} this
14762              * @param {event}
14763              */
14764         'eventleave': true,
14765         /**
14766              * @event eventclick
14767              * Fires when the mouse click an
14768              * @param {Calendar} this
14769              * @param {event}
14770              */
14771         'eventclick': true
14772         
14773     });
14774
14775 };
14776
14777 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14778     
14779      /**
14780      * @cfg {Number} startDay
14781      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14782      */
14783     startDay : 0,
14784     
14785     loadMask : false,
14786     
14787     header : false,
14788       
14789     getAutoCreate : function(){
14790         
14791         
14792         var fc_button = function(name, corner, style, content ) {
14793             return Roo.apply({},{
14794                 tag : 'span',
14795                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14796                          (corner.length ?
14797                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14798                             ''
14799                         ),
14800                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14801                 unselectable: 'on'
14802             });
14803         };
14804         
14805         var header = {};
14806         
14807         if(!this.header){
14808             header = {
14809                 tag : 'table',
14810                 cls : 'fc-header',
14811                 style : 'width:100%',
14812                 cn : [
14813                     {
14814                         tag: 'tr',
14815                         cn : [
14816                             {
14817                                 tag : 'td',
14818                                 cls : 'fc-header-left',
14819                                 cn : [
14820                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14821                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14822                                     { tag: 'span', cls: 'fc-header-space' },
14823                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14824
14825
14826                                 ]
14827                             },
14828
14829                             {
14830                                 tag : 'td',
14831                                 cls : 'fc-header-center',
14832                                 cn : [
14833                                     {
14834                                         tag: 'span',
14835                                         cls: 'fc-header-title',
14836                                         cn : {
14837                                             tag: 'H2',
14838                                             html : 'month / year'
14839                                         }
14840                                     }
14841
14842                                 ]
14843                             },
14844                             {
14845                                 tag : 'td',
14846                                 cls : 'fc-header-right',
14847                                 cn : [
14848                               /*      fc_button('month', 'left', '', 'month' ),
14849                                     fc_button('week', '', '', 'week' ),
14850                                     fc_button('day', 'right', '', 'day' )
14851                                 */    
14852
14853                                 ]
14854                             }
14855
14856                         ]
14857                     }
14858                 ]
14859             };
14860         }
14861         
14862         header = this.header;
14863         
14864        
14865         var cal_heads = function() {
14866             var ret = [];
14867             // fixme - handle this.
14868             
14869             for (var i =0; i < Date.dayNames.length; i++) {
14870                 var d = Date.dayNames[i];
14871                 ret.push({
14872                     tag: 'th',
14873                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14874                     html : d.substring(0,3)
14875                 });
14876                 
14877             }
14878             ret[0].cls += ' fc-first';
14879             ret[6].cls += ' fc-last';
14880             return ret;
14881         };
14882         var cal_cell = function(n) {
14883             return  {
14884                 tag: 'td',
14885                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14886                 cn : [
14887                     {
14888                         cn : [
14889                             {
14890                                 cls: 'fc-day-number',
14891                                 html: 'D'
14892                             },
14893                             {
14894                                 cls: 'fc-day-content',
14895                              
14896                                 cn : [
14897                                      {
14898                                         style: 'position: relative;' // height: 17px;
14899                                     }
14900                                 ]
14901                             }
14902                             
14903                             
14904                         ]
14905                     }
14906                 ]
14907                 
14908             }
14909         };
14910         var cal_rows = function() {
14911             
14912             var ret = [];
14913             for (var r = 0; r < 6; r++) {
14914                 var row= {
14915                     tag : 'tr',
14916                     cls : 'fc-week',
14917                     cn : []
14918                 };
14919                 
14920                 for (var i =0; i < Date.dayNames.length; i++) {
14921                     var d = Date.dayNames[i];
14922                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14923
14924                 }
14925                 row.cn[0].cls+=' fc-first';
14926                 row.cn[0].cn[0].style = 'min-height:90px';
14927                 row.cn[6].cls+=' fc-last';
14928                 ret.push(row);
14929                 
14930             }
14931             ret[0].cls += ' fc-first';
14932             ret[4].cls += ' fc-prev-last';
14933             ret[5].cls += ' fc-last';
14934             return ret;
14935             
14936         };
14937         
14938         var cal_table = {
14939             tag: 'table',
14940             cls: 'fc-border-separate',
14941             style : 'width:100%',
14942             cellspacing  : 0,
14943             cn : [
14944                 { 
14945                     tag: 'thead',
14946                     cn : [
14947                         { 
14948                             tag: 'tr',
14949                             cls : 'fc-first fc-last',
14950                             cn : cal_heads()
14951                         }
14952                     ]
14953                 },
14954                 { 
14955                     tag: 'tbody',
14956                     cn : cal_rows()
14957                 }
14958                   
14959             ]
14960         };
14961          
14962          var cfg = {
14963             cls : 'fc fc-ltr',
14964             cn : [
14965                 header,
14966                 {
14967                     cls : 'fc-content',
14968                     style : "position: relative;",
14969                     cn : [
14970                         {
14971                             cls : 'fc-view fc-view-month fc-grid',
14972                             style : 'position: relative',
14973                             unselectable : 'on',
14974                             cn : [
14975                                 {
14976                                     cls : 'fc-event-container',
14977                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14978                                 },
14979                                 cal_table
14980                             ]
14981                         }
14982                     ]
14983     
14984                 }
14985            ] 
14986             
14987         };
14988         
14989          
14990         
14991         return cfg;
14992     },
14993     
14994     
14995     initEvents : function()
14996     {
14997         if(!this.store){
14998             throw "can not find store for calendar";
14999         }
15000         
15001         var mark = {
15002             tag: "div",
15003             cls:"x-dlg-mask",
15004             style: "text-align:center",
15005             cn: [
15006                 {
15007                     tag: "div",
15008                     style: "background-color:white;width:50%;margin:250 auto",
15009                     cn: [
15010                         {
15011                             tag: "img",
15012                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15013                         },
15014                         {
15015                             tag: "span",
15016                             html: "Loading"
15017                         }
15018                         
15019                     ]
15020                 }
15021             ]
15022         };
15023         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15024         
15025         var size = this.el.select('.fc-content', true).first().getSize();
15026         this.maskEl.setSize(size.width, size.height);
15027         this.maskEl.enableDisplayMode("block");
15028         if(!this.loadMask){
15029             this.maskEl.hide();
15030         }
15031         
15032         this.store = Roo.factory(this.store, Roo.data);
15033         this.store.on('load', this.onLoad, this);
15034         this.store.on('beforeload', this.onBeforeLoad, this);
15035         
15036         this.resize();
15037         
15038         this.cells = this.el.select('.fc-day',true);
15039         //Roo.log(this.cells);
15040         this.textNodes = this.el.query('.fc-day-number');
15041         this.cells.addClassOnOver('fc-state-hover');
15042         
15043         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15044         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15045         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15046         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15047         
15048         this.on('monthchange', this.onMonthChange, this);
15049         
15050         this.update(new Date().clearTime());
15051     },
15052     
15053     resize : function() {
15054         var sz  = this.el.getSize();
15055         
15056         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15057         this.el.select('.fc-day-content div',true).setHeight(34);
15058     },
15059     
15060     
15061     // private
15062     showPrevMonth : function(e){
15063         this.update(this.activeDate.add("mo", -1));
15064     },
15065     showToday : function(e){
15066         this.update(new Date().clearTime());
15067     },
15068     // private
15069     showNextMonth : function(e){
15070         this.update(this.activeDate.add("mo", 1));
15071     },
15072
15073     // private
15074     showPrevYear : function(){
15075         this.update(this.activeDate.add("y", -1));
15076     },
15077
15078     // private
15079     showNextYear : function(){
15080         this.update(this.activeDate.add("y", 1));
15081     },
15082
15083     
15084    // private
15085     update : function(date)
15086     {
15087         var vd = this.activeDate;
15088         this.activeDate = date;
15089 //        if(vd && this.el){
15090 //            var t = date.getTime();
15091 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15092 //                Roo.log('using add remove');
15093 //                
15094 //                this.fireEvent('monthchange', this, date);
15095 //                
15096 //                this.cells.removeClass("fc-state-highlight");
15097 //                this.cells.each(function(c){
15098 //                   if(c.dateValue == t){
15099 //                       c.addClass("fc-state-highlight");
15100 //                       setTimeout(function(){
15101 //                            try{c.dom.firstChild.focus();}catch(e){}
15102 //                       }, 50);
15103 //                       return false;
15104 //                   }
15105 //                   return true;
15106 //                });
15107 //                return;
15108 //            }
15109 //        }
15110         
15111         var days = date.getDaysInMonth();
15112         
15113         var firstOfMonth = date.getFirstDateOfMonth();
15114         var startingPos = firstOfMonth.getDay()-this.startDay;
15115         
15116         if(startingPos < this.startDay){
15117             startingPos += 7;
15118         }
15119         
15120         var pm = date.add(Date.MONTH, -1);
15121         var prevStart = pm.getDaysInMonth()-startingPos;
15122 //        
15123         this.cells = this.el.select('.fc-day',true);
15124         this.textNodes = this.el.query('.fc-day-number');
15125         this.cells.addClassOnOver('fc-state-hover');
15126         
15127         var cells = this.cells.elements;
15128         var textEls = this.textNodes;
15129         
15130         Roo.each(cells, function(cell){
15131             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15132         });
15133         
15134         days += startingPos;
15135
15136         // convert everything to numbers so it's fast
15137         var day = 86400000;
15138         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15139         //Roo.log(d);
15140         //Roo.log(pm);
15141         //Roo.log(prevStart);
15142         
15143         var today = new Date().clearTime().getTime();
15144         var sel = date.clearTime().getTime();
15145         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15146         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15147         var ddMatch = this.disabledDatesRE;
15148         var ddText = this.disabledDatesText;
15149         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15150         var ddaysText = this.disabledDaysText;
15151         var format = this.format;
15152         
15153         var setCellClass = function(cal, cell){
15154             cell.row = 0;
15155             cell.events = [];
15156             cell.more = [];
15157             //Roo.log('set Cell Class');
15158             cell.title = "";
15159             var t = d.getTime();
15160             
15161             //Roo.log(d);
15162             
15163             cell.dateValue = t;
15164             if(t == today){
15165                 cell.className += " fc-today";
15166                 cell.className += " fc-state-highlight";
15167                 cell.title = cal.todayText;
15168             }
15169             if(t == sel){
15170                 // disable highlight in other month..
15171                 //cell.className += " fc-state-highlight";
15172                 
15173             }
15174             // disabling
15175             if(t < min) {
15176                 cell.className = " fc-state-disabled";
15177                 cell.title = cal.minText;
15178                 return;
15179             }
15180             if(t > max) {
15181                 cell.className = " fc-state-disabled";
15182                 cell.title = cal.maxText;
15183                 return;
15184             }
15185             if(ddays){
15186                 if(ddays.indexOf(d.getDay()) != -1){
15187                     cell.title = ddaysText;
15188                     cell.className = " fc-state-disabled";
15189                 }
15190             }
15191             if(ddMatch && format){
15192                 var fvalue = d.dateFormat(format);
15193                 if(ddMatch.test(fvalue)){
15194                     cell.title = ddText.replace("%0", fvalue);
15195                     cell.className = " fc-state-disabled";
15196                 }
15197             }
15198             
15199             if (!cell.initialClassName) {
15200                 cell.initialClassName = cell.dom.className;
15201             }
15202             
15203             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15204         };
15205
15206         var i = 0;
15207         
15208         for(; i < startingPos; i++) {
15209             textEls[i].innerHTML = (++prevStart);
15210             d.setDate(d.getDate()+1);
15211             
15212             cells[i].className = "fc-past fc-other-month";
15213             setCellClass(this, cells[i]);
15214         }
15215         
15216         var intDay = 0;
15217         
15218         for(; i < days; i++){
15219             intDay = i - startingPos + 1;
15220             textEls[i].innerHTML = (intDay);
15221             d.setDate(d.getDate()+1);
15222             
15223             cells[i].className = ''; // "x-date-active";
15224             setCellClass(this, cells[i]);
15225         }
15226         var extraDays = 0;
15227         
15228         for(; i < 42; i++) {
15229             textEls[i].innerHTML = (++extraDays);
15230             d.setDate(d.getDate()+1);
15231             
15232             cells[i].className = "fc-future fc-other-month";
15233             setCellClass(this, cells[i]);
15234         }
15235         
15236         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15237         
15238         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15239         
15240         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15241         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15242         
15243         if(totalRows != 6){
15244             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15245             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15246         }
15247         
15248         this.fireEvent('monthchange', this, date);
15249         
15250         
15251         /*
15252         if(!this.internalRender){
15253             var main = this.el.dom.firstChild;
15254             var w = main.offsetWidth;
15255             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15256             Roo.fly(main).setWidth(w);
15257             this.internalRender = true;
15258             // opera does not respect the auto grow header center column
15259             // then, after it gets a width opera refuses to recalculate
15260             // without a second pass
15261             if(Roo.isOpera && !this.secondPass){
15262                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15263                 this.secondPass = true;
15264                 this.update.defer(10, this, [date]);
15265             }
15266         }
15267         */
15268         
15269     },
15270     
15271     findCell : function(dt) {
15272         dt = dt.clearTime().getTime();
15273         var ret = false;
15274         this.cells.each(function(c){
15275             //Roo.log("check " +c.dateValue + '?=' + dt);
15276             if(c.dateValue == dt){
15277                 ret = c;
15278                 return false;
15279             }
15280             return true;
15281         });
15282         
15283         return ret;
15284     },
15285     
15286     findCells : function(ev) {
15287         var s = ev.start.clone().clearTime().getTime();
15288        // Roo.log(s);
15289         var e= ev.end.clone().clearTime().getTime();
15290        // Roo.log(e);
15291         var ret = [];
15292         this.cells.each(function(c){
15293              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15294             
15295             if(c.dateValue > e){
15296                 return ;
15297             }
15298             if(c.dateValue < s){
15299                 return ;
15300             }
15301             ret.push(c);
15302         });
15303         
15304         return ret;    
15305     },
15306     
15307 //    findBestRow: function(cells)
15308 //    {
15309 //        var ret = 0;
15310 //        
15311 //        for (var i =0 ; i < cells.length;i++) {
15312 //            ret  = Math.max(cells[i].rows || 0,ret);
15313 //        }
15314 //        return ret;
15315 //        
15316 //    },
15317     
15318     
15319     addItem : function(ev)
15320     {
15321         // look for vertical location slot in
15322         var cells = this.findCells(ev);
15323         
15324 //        ev.row = this.findBestRow(cells);
15325         
15326         // work out the location.
15327         
15328         var crow = false;
15329         var rows = [];
15330         for(var i =0; i < cells.length; i++) {
15331             
15332             cells[i].row = cells[0].row;
15333             
15334             if(i == 0){
15335                 cells[i].row = cells[i].row + 1;
15336             }
15337             
15338             if (!crow) {
15339                 crow = {
15340                     start : cells[i],
15341                     end :  cells[i]
15342                 };
15343                 continue;
15344             }
15345             if (crow.start.getY() == cells[i].getY()) {
15346                 // on same row.
15347                 crow.end = cells[i];
15348                 continue;
15349             }
15350             // different row.
15351             rows.push(crow);
15352             crow = {
15353                 start: cells[i],
15354                 end : cells[i]
15355             };
15356             
15357         }
15358         
15359         rows.push(crow);
15360         ev.els = [];
15361         ev.rows = rows;
15362         ev.cells = cells;
15363         
15364         cells[0].events.push(ev);
15365         
15366         this.calevents.push(ev);
15367     },
15368     
15369     clearEvents: function() {
15370         
15371         if(!this.calevents){
15372             return;
15373         }
15374         
15375         Roo.each(this.cells.elements, function(c){
15376             c.row = 0;
15377             c.events = [];
15378             c.more = [];
15379         });
15380         
15381         Roo.each(this.calevents, function(e) {
15382             Roo.each(e.els, function(el) {
15383                 el.un('mouseenter' ,this.onEventEnter, this);
15384                 el.un('mouseleave' ,this.onEventLeave, this);
15385                 el.remove();
15386             },this);
15387         },this);
15388         
15389         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15390             e.remove();
15391         });
15392         
15393     },
15394     
15395     renderEvents: function()
15396     {   
15397         var _this = this;
15398         
15399         this.cells.each(function(c) {
15400             
15401             if(c.row < 5){
15402                 return;
15403             }
15404             
15405             var ev = c.events;
15406             
15407             var r = 4;
15408             if(c.row != c.events.length){
15409                 r = 4 - (4 - (c.row - c.events.length));
15410             }
15411             
15412             c.events = ev.slice(0, r);
15413             c.more = ev.slice(r);
15414             
15415             if(c.more.length && c.more.length == 1){
15416                 c.events.push(c.more.pop());
15417             }
15418             
15419             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15420             
15421         });
15422             
15423         this.cells.each(function(c) {
15424             
15425             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15426             
15427             
15428             for (var e = 0; e < c.events.length; e++){
15429                 var ev = c.events[e];
15430                 var rows = ev.rows;
15431                 
15432                 for(var i = 0; i < rows.length; i++) {
15433                 
15434                     // how many rows should it span..
15435
15436                     var  cfg = {
15437                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15438                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15439
15440                         unselectable : "on",
15441                         cn : [
15442                             {
15443                                 cls: 'fc-event-inner',
15444                                 cn : [
15445     //                                {
15446     //                                  tag:'span',
15447     //                                  cls: 'fc-event-time',
15448     //                                  html : cells.length > 1 ? '' : ev.time
15449     //                                },
15450                                     {
15451                                       tag:'span',
15452                                       cls: 'fc-event-title',
15453                                       html : String.format('{0}', ev.title)
15454                                     }
15455
15456
15457                                 ]
15458                             },
15459                             {
15460                                 cls: 'ui-resizable-handle ui-resizable-e',
15461                                 html : '&nbsp;&nbsp;&nbsp'
15462                             }
15463
15464                         ]
15465                     };
15466
15467                     if (i == 0) {
15468                         cfg.cls += ' fc-event-start';
15469                     }
15470                     if ((i+1) == rows.length) {
15471                         cfg.cls += ' fc-event-end';
15472                     }
15473
15474                     var ctr = _this.el.select('.fc-event-container',true).first();
15475                     var cg = ctr.createChild(cfg);
15476
15477                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15478                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15479
15480                     var r = (c.more.length) ? 1 : 0;
15481                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15482                     cg.setWidth(ebox.right - sbox.x -2);
15483
15484                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15485                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15486                     cg.on('click', _this.onEventClick, _this, ev);
15487
15488                     ev.els.push(cg);
15489                     
15490                 }
15491                 
15492             }
15493             
15494             
15495             if(c.more.length){
15496                 var  cfg = {
15497                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15498                     style : 'position: absolute',
15499                     unselectable : "on",
15500                     cn : [
15501                         {
15502                             cls: 'fc-event-inner',
15503                             cn : [
15504                                 {
15505                                   tag:'span',
15506                                   cls: 'fc-event-title',
15507                                   html : 'More'
15508                                 }
15509
15510
15511                             ]
15512                         },
15513                         {
15514                             cls: 'ui-resizable-handle ui-resizable-e',
15515                             html : '&nbsp;&nbsp;&nbsp'
15516                         }
15517
15518                     ]
15519                 };
15520
15521                 var ctr = _this.el.select('.fc-event-container',true).first();
15522                 var cg = ctr.createChild(cfg);
15523
15524                 var sbox = c.select('.fc-day-content',true).first().getBox();
15525                 var ebox = c.select('.fc-day-content',true).first().getBox();
15526                 //Roo.log(cg);
15527                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15528                 cg.setWidth(ebox.right - sbox.x -2);
15529
15530                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15531                 
15532             }
15533             
15534         });
15535         
15536         
15537         
15538     },
15539     
15540     onEventEnter: function (e, el,event,d) {
15541         this.fireEvent('evententer', this, el, event);
15542     },
15543     
15544     onEventLeave: function (e, el,event,d) {
15545         this.fireEvent('eventleave', this, el, event);
15546     },
15547     
15548     onEventClick: function (e, el,event,d) {
15549         this.fireEvent('eventclick', this, el, event);
15550     },
15551     
15552     onMonthChange: function () {
15553         this.store.load();
15554     },
15555     
15556     onMoreEventClick: function(e, el, more)
15557     {
15558         var _this = this;
15559         
15560         this.calpopover.placement = 'right';
15561         this.calpopover.setTitle('More');
15562         
15563         this.calpopover.setContent('');
15564         
15565         var ctr = this.calpopover.el.select('.popover-content', true).first();
15566         
15567         Roo.each(more, function(m){
15568             var cfg = {
15569                 cls : 'fc-event-hori fc-event-draggable',
15570                 html : m.title
15571             };
15572             var cg = ctr.createChild(cfg);
15573             
15574             cg.on('click', _this.onEventClick, _this, m);
15575         });
15576         
15577         this.calpopover.show(el);
15578         
15579         
15580     },
15581     
15582     onLoad: function () 
15583     {   
15584         this.calevents = [];
15585         var cal = this;
15586         
15587         if(this.store.getCount() > 0){
15588             this.store.data.each(function(d){
15589                cal.addItem({
15590                     id : d.data.id,
15591                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15592                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15593                     time : d.data.start_time,
15594                     title : d.data.title,
15595                     description : d.data.description,
15596                     venue : d.data.venue
15597                 });
15598             });
15599         }
15600         
15601         this.renderEvents();
15602         
15603         if(this.calevents.length && this.loadMask){
15604             this.maskEl.hide();
15605         }
15606     },
15607     
15608     onBeforeLoad: function()
15609     {
15610         this.clearEvents();
15611         if(this.loadMask){
15612             this.maskEl.show();
15613         }
15614     }
15615 });
15616
15617  
15618  /*
15619  * - LGPL
15620  *
15621  * element
15622  * 
15623  */
15624
15625 /**
15626  * @class Roo.bootstrap.Popover
15627  * @extends Roo.bootstrap.Component
15628  * Bootstrap Popover class
15629  * @cfg {String} html contents of the popover   (or false to use children..)
15630  * @cfg {String} title of popover (or false to hide)
15631  * @cfg {String} placement how it is placed
15632  * @cfg {String} trigger click || hover (or false to trigger manually)
15633  * @cfg {String} over what (parent or false to trigger manually.)
15634  * @cfg {Number} delay - delay before showing
15635  
15636  * @constructor
15637  * Create a new Popover
15638  * @param {Object} config The config object
15639  */
15640
15641 Roo.bootstrap.Popover = function(config){
15642     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15643 };
15644
15645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15646     
15647     title: 'Fill in a title',
15648     html: false,
15649     
15650     placement : 'right',
15651     trigger : 'hover', // hover
15652     
15653     delay : 0,
15654     
15655     over: 'parent',
15656     
15657     can_build_overlaid : false,
15658     
15659     getChildContainer : function()
15660     {
15661         return this.el.select('.popover-content',true).first();
15662     },
15663     
15664     getAutoCreate : function(){
15665          
15666         var cfg = {
15667            cls : 'popover roo-dynamic',
15668            style: 'display:block',
15669            cn : [
15670                 {
15671                     cls : 'arrow'
15672                 },
15673                 {
15674                     cls : 'popover-inner',
15675                     cn : [
15676                         {
15677                             tag: 'h3',
15678                             cls: 'popover-title',
15679                             html : this.title
15680                         },
15681                         {
15682                             cls : 'popover-content',
15683                             html : this.html
15684                         }
15685                     ]
15686                     
15687                 }
15688            ]
15689         };
15690         
15691         return cfg;
15692     },
15693     setTitle: function(str)
15694     {
15695         this.title = str;
15696         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15697     },
15698     setContent: function(str)
15699     {
15700         this.html = str;
15701         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15702     },
15703     // as it get's added to the bottom of the page.
15704     onRender : function(ct, position)
15705     {
15706         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15707         if(!this.el){
15708             var cfg = Roo.apply({},  this.getAutoCreate());
15709             cfg.id = Roo.id();
15710             
15711             if (this.cls) {
15712                 cfg.cls += ' ' + this.cls;
15713             }
15714             if (this.style) {
15715                 cfg.style = this.style;
15716             }
15717             //Roo.log("adding to ");
15718             this.el = Roo.get(document.body).createChild(cfg, position);
15719 //            Roo.log(this.el);
15720         }
15721         this.initEvents();
15722     },
15723     
15724     initEvents : function()
15725     {
15726         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15727         this.el.enableDisplayMode('block');
15728         this.el.hide();
15729         if (this.over === false) {
15730             return; 
15731         }
15732         if (this.triggers === false) {
15733             return;
15734         }
15735         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15736         var triggers = this.trigger ? this.trigger.split(' ') : [];
15737         Roo.each(triggers, function(trigger) {
15738         
15739             if (trigger == 'click') {
15740                 on_el.on('click', this.toggle, this);
15741             } else if (trigger != 'manual') {
15742                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15743                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15744       
15745                 on_el.on(eventIn  ,this.enter, this);
15746                 on_el.on(eventOut, this.leave, this);
15747             }
15748         }, this);
15749         
15750     },
15751     
15752     
15753     // private
15754     timeout : null,
15755     hoverState : null,
15756     
15757     toggle : function () {
15758         this.hoverState == 'in' ? this.leave() : this.enter();
15759     },
15760     
15761     enter : function () {
15762        
15763     
15764         clearTimeout(this.timeout);
15765     
15766         this.hoverState = 'in';
15767     
15768         if (!this.delay || !this.delay.show) {
15769             this.show();
15770             return;
15771         }
15772         var _t = this;
15773         this.timeout = setTimeout(function () {
15774             if (_t.hoverState == 'in') {
15775                 _t.show();
15776             }
15777         }, this.delay.show)
15778     },
15779     leave : function() {
15780         clearTimeout(this.timeout);
15781     
15782         this.hoverState = 'out';
15783     
15784         if (!this.delay || !this.delay.hide) {
15785             this.hide();
15786             return;
15787         }
15788         var _t = this;
15789         this.timeout = setTimeout(function () {
15790             if (_t.hoverState == 'out') {
15791                 _t.hide();
15792             }
15793         }, this.delay.hide)
15794     },
15795     
15796     show : function (on_el)
15797     {
15798         if (!on_el) {
15799             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15800         }
15801         // set content.
15802         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15803         if (this.html !== false) {
15804             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15805         }
15806         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15807         if (!this.title.length) {
15808             this.el.select('.popover-title',true).hide();
15809         }
15810         
15811         var placement = typeof this.placement == 'function' ?
15812             this.placement.call(this, this.el, on_el) :
15813             this.placement;
15814             
15815         var autoToken = /\s?auto?\s?/i;
15816         var autoPlace = autoToken.test(placement);
15817         if (autoPlace) {
15818             placement = placement.replace(autoToken, '') || 'top';
15819         }
15820         
15821         //this.el.detach()
15822         //this.el.setXY([0,0]);
15823         this.el.show();
15824         this.el.dom.style.display='block';
15825         this.el.addClass(placement);
15826         
15827         //this.el.appendTo(on_el);
15828         
15829         var p = this.getPosition();
15830         var box = this.el.getBox();
15831         
15832         if (autoPlace) {
15833             // fixme..
15834         }
15835         var align = Roo.bootstrap.Popover.alignment[placement];
15836         this.el.alignTo(on_el, align[0],align[1]);
15837         //var arrow = this.el.select('.arrow',true).first();
15838         //arrow.set(align[2], 
15839         
15840         this.el.addClass('in');
15841         
15842         
15843         if (this.el.hasClass('fade')) {
15844             // fade it?
15845         }
15846         
15847     },
15848     hide : function()
15849     {
15850         this.el.setXY([0,0]);
15851         this.el.removeClass('in');
15852         this.el.hide();
15853         this.hoverState = null;
15854         
15855     }
15856     
15857 });
15858
15859 Roo.bootstrap.Popover.alignment = {
15860     'left' : ['r-l', [-10,0], 'right'],
15861     'right' : ['l-r', [10,0], 'left'],
15862     'bottom' : ['t-b', [0,10], 'top'],
15863     'top' : [ 'b-t', [0,-10], 'bottom']
15864 };
15865
15866  /*
15867  * - LGPL
15868  *
15869  * Progress
15870  * 
15871  */
15872
15873 /**
15874  * @class Roo.bootstrap.Progress
15875  * @extends Roo.bootstrap.Component
15876  * Bootstrap Progress class
15877  * @cfg {Boolean} striped striped of the progress bar
15878  * @cfg {Boolean} active animated of the progress bar
15879  * 
15880  * 
15881  * @constructor
15882  * Create a new Progress
15883  * @param {Object} config The config object
15884  */
15885
15886 Roo.bootstrap.Progress = function(config){
15887     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15888 };
15889
15890 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15891     
15892     striped : false,
15893     active: false,
15894     
15895     getAutoCreate : function(){
15896         var cfg = {
15897             tag: 'div',
15898             cls: 'progress'
15899         };
15900         
15901         
15902         if(this.striped){
15903             cfg.cls += ' progress-striped';
15904         }
15905       
15906         if(this.active){
15907             cfg.cls += ' active';
15908         }
15909         
15910         
15911         return cfg;
15912     }
15913    
15914 });
15915
15916  
15917
15918  /*
15919  * - LGPL
15920  *
15921  * ProgressBar
15922  * 
15923  */
15924
15925 /**
15926  * @class Roo.bootstrap.ProgressBar
15927  * @extends Roo.bootstrap.Component
15928  * Bootstrap ProgressBar class
15929  * @cfg {Number} aria_valuenow aria-value now
15930  * @cfg {Number} aria_valuemin aria-value min
15931  * @cfg {Number} aria_valuemax aria-value max
15932  * @cfg {String} label label for the progress bar
15933  * @cfg {String} panel (success | info | warning | danger )
15934  * @cfg {String} role role of the progress bar
15935  * @cfg {String} sr_only text
15936  * 
15937  * 
15938  * @constructor
15939  * Create a new ProgressBar
15940  * @param {Object} config The config object
15941  */
15942
15943 Roo.bootstrap.ProgressBar = function(config){
15944     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15945 };
15946
15947 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15948     
15949     aria_valuenow : 0,
15950     aria_valuemin : 0,
15951     aria_valuemax : 100,
15952     label : false,
15953     panel : false,
15954     role : false,
15955     sr_only: false,
15956     
15957     getAutoCreate : function()
15958     {
15959         
15960         var cfg = {
15961             tag: 'div',
15962             cls: 'progress-bar',
15963             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15964         };
15965         
15966         if(this.sr_only){
15967             cfg.cn = {
15968                 tag: 'span',
15969                 cls: 'sr-only',
15970                 html: this.sr_only
15971             }
15972         }
15973         
15974         if(this.role){
15975             cfg.role = this.role;
15976         }
15977         
15978         if(this.aria_valuenow){
15979             cfg['aria-valuenow'] = this.aria_valuenow;
15980         }
15981         
15982         if(this.aria_valuemin){
15983             cfg['aria-valuemin'] = this.aria_valuemin;
15984         }
15985         
15986         if(this.aria_valuemax){
15987             cfg['aria-valuemax'] = this.aria_valuemax;
15988         }
15989         
15990         if(this.label && !this.sr_only){
15991             cfg.html = this.label;
15992         }
15993         
15994         if(this.panel){
15995             cfg.cls += ' progress-bar-' + this.panel;
15996         }
15997         
15998         return cfg;
15999     },
16000     
16001     update : function(aria_valuenow)
16002     {
16003         this.aria_valuenow = aria_valuenow;
16004         
16005         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16006     }
16007    
16008 });
16009
16010  
16011
16012  /*
16013  * - LGPL
16014  *
16015  * column
16016  * 
16017  */
16018
16019 /**
16020  * @class Roo.bootstrap.TabGroup
16021  * @extends Roo.bootstrap.Column
16022  * Bootstrap Column class
16023  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16024  * @cfg {Boolean} carousel true to make the group behave like a carousel
16025  * @cfg {Boolean} bullets show bullets for the panels
16026  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16027  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16028  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16029  * 
16030  * @constructor
16031  * Create a new TabGroup
16032  * @param {Object} config The config object
16033  */
16034
16035 Roo.bootstrap.TabGroup = function(config){
16036     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16037     if (!this.navId) {
16038         this.navId = Roo.id();
16039     }
16040     this.tabs = [];
16041     Roo.bootstrap.TabGroup.register(this);
16042     
16043 };
16044
16045 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16046     
16047     carousel : false,
16048     transition : false,
16049     bullets : 0,
16050     timer : 0,
16051     autoslide : false,
16052     slideFn : false,
16053     slideOnTouch : false,
16054     
16055     getAutoCreate : function()
16056     {
16057         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16058         
16059         cfg.cls += ' tab-content';
16060         
16061         if (this.carousel) {
16062             cfg.cls += ' carousel slide';
16063             
16064             cfg.cn = [{
16065                cls : 'carousel-inner'
16066             }];
16067         
16068             if(this.bullets  && !Roo.isTouch){
16069                 
16070                 var bullets = {
16071                     cls : 'carousel-bullets',
16072                     cn : []
16073                 };
16074                
16075                 if(this.bullets_cls){
16076                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16077                 }
16078                  /*
16079                 for (var i = 0; i < this.bullets; i++){
16080                     bullets.cn.push({
16081                         cls : 'bullet bullet-' + i
16082                     });
16083                 }
16084                 */
16085                 bullets.cn.push({
16086                     cls : 'clear'
16087                 });
16088                 
16089                 cfg.cn[0].cn = bullets;
16090             }
16091         }
16092         
16093         return cfg;
16094     },
16095     
16096     initEvents:  function()
16097     {
16098         if(Roo.isTouch && this.slideOnTouch){
16099             this.el.on("touchstart", this.onTouchStart, this);
16100         }
16101         
16102         if(this.autoslide){
16103             var _this = this;
16104             
16105             this.slideFn = window.setInterval(function() {
16106                 _this.showPanelNext();
16107             }, this.timer);
16108         }
16109         
16110     },
16111     
16112     onTouchStart : function(e, el, o)
16113     {
16114         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16115             return;
16116         }
16117         
16118         this.showPanelNext();
16119     },
16120     
16121     getChildContainer : function()
16122     {
16123         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16124     },
16125     
16126     /**
16127     * register a Navigation item
16128     * @param {Roo.bootstrap.NavItem} the navitem to add
16129     */
16130     register : function(item)
16131     {
16132         this.tabs.push( item);
16133         item.navId = this.navId; // not really needed..
16134         this.addBullet();
16135     
16136     },
16137     
16138     getActivePanel : function()
16139     {
16140         var r = false;
16141         Roo.each(this.tabs, function(t) {
16142             if (t.active) {
16143                 r = t;
16144                 return false;
16145             }
16146             return null;
16147         });
16148         return r;
16149         
16150     },
16151     getPanelByName : function(n)
16152     {
16153         var r = false;
16154         Roo.each(this.tabs, function(t) {
16155             if (t.tabId == n) {
16156                 r = t;
16157                 return false;
16158             }
16159             return null;
16160         });
16161         return r;
16162     },
16163     indexOfPanel : function(p)
16164     {
16165         var r = false;
16166         Roo.each(this.tabs, function(t,i) {
16167             if (t.tabId == p.tabId) {
16168                 r = i;
16169                 return false;
16170             }
16171             return null;
16172         });
16173         return r;
16174     },
16175     /**
16176      * show a specific panel
16177      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16178      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16179      */
16180     showPanel : function (pan)
16181     {
16182         if(this.transition || typeof(pan) == 'undefined'){
16183             Roo.log("waiting for the transitionend");
16184             return;
16185         }
16186         
16187         if (typeof(pan) == 'number') {
16188             pan = this.tabs[pan];
16189         }
16190         
16191         if (typeof(pan) == 'string') {
16192             pan = this.getPanelByName(pan);
16193         }
16194         
16195         var cur = this.getActivePanel();
16196         
16197         if(!pan || !cur){
16198             Roo.log('pan or acitve pan is undefined');
16199             return false;
16200         }
16201         
16202         if (pan.tabId == this.getActivePanel().tabId) {
16203             return true;
16204         }
16205         
16206         if (false === cur.fireEvent('beforedeactivate')) {
16207             return false;
16208         }
16209         
16210         if(this.bullets > 0 && !Roo.isTouch){
16211             this.setActiveBullet(this.indexOfPanel(pan));
16212         }
16213         
16214         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16215             
16216             this.transition = true;
16217             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16218             var lr = dir == 'next' ? 'left' : 'right';
16219             pan.el.addClass(dir); // or prev
16220             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16221             cur.el.addClass(lr); // or right
16222             pan.el.addClass(lr);
16223             
16224             var _this = this;
16225             cur.el.on('transitionend', function() {
16226                 Roo.log("trans end?");
16227                 
16228                 pan.el.removeClass([lr,dir]);
16229                 pan.setActive(true);
16230                 
16231                 cur.el.removeClass([lr]);
16232                 cur.setActive(false);
16233                 
16234                 _this.transition = false;
16235                 
16236             }, this, { single:  true } );
16237             
16238             return true;
16239         }
16240         
16241         cur.setActive(false);
16242         pan.setActive(true);
16243         
16244         return true;
16245         
16246     },
16247     showPanelNext : function()
16248     {
16249         var i = this.indexOfPanel(this.getActivePanel());
16250         
16251         if (i >= this.tabs.length - 1 && !this.autoslide) {
16252             return;
16253         }
16254         
16255         if (i >= this.tabs.length - 1 && this.autoslide) {
16256             i = -1;
16257         }
16258         
16259         this.showPanel(this.tabs[i+1]);
16260     },
16261     
16262     showPanelPrev : function()
16263     {
16264         var i = this.indexOfPanel(this.getActivePanel());
16265         
16266         if (i  < 1 && !this.autoslide) {
16267             return;
16268         }
16269         
16270         if (i < 1 && this.autoslide) {
16271             i = this.tabs.length;
16272         }
16273         
16274         this.showPanel(this.tabs[i-1]);
16275     },
16276     
16277     
16278     addBullet: function()
16279     {
16280         if(!this.bullets || Roo.isTouch){
16281             return;
16282         }
16283         var ctr = this.el.select('.carousel-bullets',true).first();
16284         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16285         var bullet = ctr.createChild({
16286             cls : 'bullet bullet-' + i
16287         },ctr.dom.lastChild);
16288         
16289         
16290         var _this = this;
16291         
16292         bullet.on('click', (function(e, el, o, ii, t){
16293
16294             e.preventDefault();
16295
16296             this.showPanel(ii);
16297
16298             if(this.autoslide && this.slideFn){
16299                 clearInterval(this.slideFn);
16300                 this.slideFn = window.setInterval(function() {
16301                     _this.showPanelNext();
16302                 }, this.timer);
16303             }
16304
16305         }).createDelegate(this, [i, bullet], true));
16306                 
16307         
16308     },
16309      
16310     setActiveBullet : function(i)
16311     {
16312         if(Roo.isTouch){
16313             return;
16314         }
16315         
16316         Roo.each(this.el.select('.bullet', true).elements, function(el){
16317             el.removeClass('selected');
16318         });
16319
16320         var bullet = this.el.select('.bullet-' + i, true).first();
16321         
16322         if(!bullet){
16323             return;
16324         }
16325         
16326         bullet.addClass('selected');
16327     }
16328     
16329     
16330   
16331 });
16332
16333  
16334
16335  
16336  
16337 Roo.apply(Roo.bootstrap.TabGroup, {
16338     
16339     groups: {},
16340      /**
16341     * register a Navigation Group
16342     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16343     */
16344     register : function(navgrp)
16345     {
16346         this.groups[navgrp.navId] = navgrp;
16347         
16348     },
16349     /**
16350     * fetch a Navigation Group based on the navigation ID
16351     * if one does not exist , it will get created.
16352     * @param {string} the navgroup to add
16353     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16354     */
16355     get: function(navId) {
16356         if (typeof(this.groups[navId]) == 'undefined') {
16357             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16358         }
16359         return this.groups[navId] ;
16360     }
16361     
16362     
16363     
16364 });
16365
16366  /*
16367  * - LGPL
16368  *
16369  * TabPanel
16370  * 
16371  */
16372
16373 /**
16374  * @class Roo.bootstrap.TabPanel
16375  * @extends Roo.bootstrap.Component
16376  * Bootstrap TabPanel class
16377  * @cfg {Boolean} active panel active
16378  * @cfg {String} html panel content
16379  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16380  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16381  * 
16382  * 
16383  * @constructor
16384  * Create a new TabPanel
16385  * @param {Object} config The config object
16386  */
16387
16388 Roo.bootstrap.TabPanel = function(config){
16389     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16390     this.addEvents({
16391         /**
16392              * @event changed
16393              * Fires when the active status changes
16394              * @param {Roo.bootstrap.TabPanel} this
16395              * @param {Boolean} state the new state
16396             
16397          */
16398         'changed': true,
16399         /**
16400              * @event beforedeactivate
16401              * Fires before a tab is de-activated - can be used to do validation on a form.
16402              * @param {Roo.bootstrap.TabPanel} this
16403              * @return {Boolean} false if there is an error
16404             
16405          */
16406         'beforedeactivate': true
16407      });
16408     
16409     this.tabId = this.tabId || Roo.id();
16410   
16411 };
16412
16413 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16414     
16415     active: false,
16416     html: false,
16417     tabId: false,
16418     navId : false,
16419     
16420     getAutoCreate : function(){
16421         var cfg = {
16422             tag: 'div',
16423             // item is needed for carousel - not sure if it has any effect otherwise
16424             cls: 'tab-pane item',
16425             html: this.html || ''
16426         };
16427         
16428         if(this.active){
16429             cfg.cls += ' active';
16430         }
16431         
16432         if(this.tabId){
16433             cfg.tabId = this.tabId;
16434         }
16435         
16436         
16437         return cfg;
16438     },
16439     
16440     initEvents:  function()
16441     {
16442         var p = this.parent();
16443         this.navId = this.navId || p.navId;
16444         
16445         if (typeof(this.navId) != 'undefined') {
16446             // not really needed.. but just in case.. parent should be a NavGroup.
16447             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16448             
16449             tg.register(this);
16450             
16451             var i = tg.tabs.length - 1;
16452             
16453             if(this.active && tg.bullets > 0 && i < tg.bullets){
16454                 tg.setActiveBullet(i);
16455             }
16456         }
16457         
16458     },
16459     
16460     
16461     onRender : function(ct, position)
16462     {
16463        // Roo.log("Call onRender: " + this.xtype);
16464         
16465         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16466         
16467         
16468         
16469         
16470         
16471     },
16472     
16473     setActive: function(state)
16474     {
16475         Roo.log("panel - set active " + this.tabId + "=" + state);
16476         
16477         this.active = state;
16478         if (!state) {
16479             this.el.removeClass('active');
16480             
16481         } else  if (!this.el.hasClass('active')) {
16482             this.el.addClass('active');
16483         }
16484         
16485         this.fireEvent('changed', this, state);
16486     }
16487     
16488     
16489 });
16490  
16491
16492  
16493
16494  /*
16495  * - LGPL
16496  *
16497  * DateField
16498  * 
16499  */
16500
16501 /**
16502  * @class Roo.bootstrap.DateField
16503  * @extends Roo.bootstrap.Input
16504  * Bootstrap DateField class
16505  * @cfg {Number} weekStart default 0
16506  * @cfg {String} viewMode default empty, (months|years)
16507  * @cfg {String} minViewMode default empty, (months|years)
16508  * @cfg {Number} startDate default -Infinity
16509  * @cfg {Number} endDate default Infinity
16510  * @cfg {Boolean} todayHighlight default false
16511  * @cfg {Boolean} todayBtn default false
16512  * @cfg {Boolean} calendarWeeks default false
16513  * @cfg {Object} daysOfWeekDisabled default empty
16514  * @cfg {Boolean} singleMode default false (true | false)
16515  * 
16516  * @cfg {Boolean} keyboardNavigation default true
16517  * @cfg {String} language default en
16518  * 
16519  * @constructor
16520  * Create a new DateField
16521  * @param {Object} config The config object
16522  */
16523
16524 Roo.bootstrap.DateField = function(config){
16525     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16526      this.addEvents({
16527             /**
16528              * @event show
16529              * Fires when this field show.
16530              * @param {Roo.bootstrap.DateField} this
16531              * @param {Mixed} date The date value
16532              */
16533             show : true,
16534             /**
16535              * @event show
16536              * Fires when this field hide.
16537              * @param {Roo.bootstrap.DateField} this
16538              * @param {Mixed} date The date value
16539              */
16540             hide : true,
16541             /**
16542              * @event select
16543              * Fires when select a date.
16544              * @param {Roo.bootstrap.DateField} this
16545              * @param {Mixed} date The date value
16546              */
16547             select : true,
16548             /**
16549              * @event beforeselect
16550              * Fires when before select a date.
16551              * @param {Roo.bootstrap.DateField} this
16552              * @param {Mixed} date The date value
16553              */
16554             beforeselect : true
16555         });
16556 };
16557
16558 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16559     
16560     /**
16561      * @cfg {String} format
16562      * The default date format string which can be overriden for localization support.  The format must be
16563      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16564      */
16565     format : "m/d/y",
16566     /**
16567      * @cfg {String} altFormats
16568      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16569      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16570      */
16571     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16572     
16573     weekStart : 0,
16574     
16575     viewMode : '',
16576     
16577     minViewMode : '',
16578     
16579     todayHighlight : false,
16580     
16581     todayBtn: false,
16582     
16583     language: 'en',
16584     
16585     keyboardNavigation: true,
16586     
16587     calendarWeeks: false,
16588     
16589     startDate: -Infinity,
16590     
16591     endDate: Infinity,
16592     
16593     daysOfWeekDisabled: [],
16594     
16595     _events: [],
16596     
16597     singleMode : false,
16598     
16599     UTCDate: function()
16600     {
16601         return new Date(Date.UTC.apply(Date, arguments));
16602     },
16603     
16604     UTCToday: function()
16605     {
16606         var today = new Date();
16607         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16608     },
16609     
16610     getDate: function() {
16611             var d = this.getUTCDate();
16612             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16613     },
16614     
16615     getUTCDate: function() {
16616             return this.date;
16617     },
16618     
16619     setDate: function(d) {
16620             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16621     },
16622     
16623     setUTCDate: function(d) {
16624             this.date = d;
16625             this.setValue(this.formatDate(this.date));
16626     },
16627         
16628     onRender: function(ct, position)
16629     {
16630         
16631         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16632         
16633         this.language = this.language || 'en';
16634         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16635         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16636         
16637         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16638         this.format = this.format || 'm/d/y';
16639         this.isInline = false;
16640         this.isInput = true;
16641         this.component = this.el.select('.add-on', true).first() || false;
16642         this.component = (this.component && this.component.length === 0) ? false : this.component;
16643         this.hasInput = this.component && this.inputEL().length;
16644         
16645         if (typeof(this.minViewMode === 'string')) {
16646             switch (this.minViewMode) {
16647                 case 'months':
16648                     this.minViewMode = 1;
16649                     break;
16650                 case 'years':
16651                     this.minViewMode = 2;
16652                     break;
16653                 default:
16654                     this.minViewMode = 0;
16655                     break;
16656             }
16657         }
16658         
16659         if (typeof(this.viewMode === 'string')) {
16660             switch (this.viewMode) {
16661                 case 'months':
16662                     this.viewMode = 1;
16663                     break;
16664                 case 'years':
16665                     this.viewMode = 2;
16666                     break;
16667                 default:
16668                     this.viewMode = 0;
16669                     break;
16670             }
16671         }
16672                 
16673         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16674         
16675 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16676         
16677         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16678         
16679         this.picker().on('mousedown', this.onMousedown, this);
16680         this.picker().on('click', this.onClick, this);
16681         
16682         this.picker().addClass('datepicker-dropdown');
16683         
16684         this.startViewMode = this.viewMode;
16685         
16686         if(this.singleMode){
16687             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16688                 v.setVisibilityMode(Roo.Element.DISPLAY);
16689                 v.hide();
16690             });
16691             
16692             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16693                 v.setStyle('width', '189px');
16694             });
16695         }
16696         
16697         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16698             if(!this.calendarWeeks){
16699                 v.remove();
16700                 return;
16701             }
16702             
16703             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16704             v.attr('colspan', function(i, val){
16705                 return parseInt(val) + 1;
16706             });
16707         });
16708                         
16709         
16710         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16711         
16712         this.setStartDate(this.startDate);
16713         this.setEndDate(this.endDate);
16714         
16715         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16716         
16717         this.fillDow();
16718         this.fillMonths();
16719         this.update();
16720         this.showMode();
16721         
16722         if(this.isInline) {
16723             this.show();
16724         }
16725     },
16726     
16727     picker : function()
16728     {
16729         return this.pickerEl;
16730 //        return this.el.select('.datepicker', true).first();
16731     },
16732     
16733     fillDow: function()
16734     {
16735         var dowCnt = this.weekStart;
16736         
16737         var dow = {
16738             tag: 'tr',
16739             cn: [
16740                 
16741             ]
16742         };
16743         
16744         if(this.calendarWeeks){
16745             dow.cn.push({
16746                 tag: 'th',
16747                 cls: 'cw',
16748                 html: '&nbsp;'
16749             })
16750         }
16751         
16752         while (dowCnt < this.weekStart + 7) {
16753             dow.cn.push({
16754                 tag: 'th',
16755                 cls: 'dow',
16756                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16757             });
16758         }
16759         
16760         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16761     },
16762     
16763     fillMonths: function()
16764     {    
16765         var i = 0;
16766         var months = this.picker().select('>.datepicker-months td', true).first();
16767         
16768         months.dom.innerHTML = '';
16769         
16770         while (i < 12) {
16771             var month = {
16772                 tag: 'span',
16773                 cls: 'month',
16774                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16775             };
16776             
16777             months.createChild(month);
16778         }
16779         
16780     },
16781     
16782     update: function()
16783     {
16784         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;
16785         
16786         if (this.date < this.startDate) {
16787             this.viewDate = new Date(this.startDate);
16788         } else if (this.date > this.endDate) {
16789             this.viewDate = new Date(this.endDate);
16790         } else {
16791             this.viewDate = new Date(this.date);
16792         }
16793         
16794         this.fill();
16795     },
16796     
16797     fill: function() 
16798     {
16799         var d = new Date(this.viewDate),
16800                 year = d.getUTCFullYear(),
16801                 month = d.getUTCMonth(),
16802                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16803                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16804                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16805                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16806                 currentDate = this.date && this.date.valueOf(),
16807                 today = this.UTCToday();
16808         
16809         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16810         
16811 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16812         
16813 //        this.picker.select('>tfoot th.today').
16814 //                                              .text(dates[this.language].today)
16815 //                                              .toggle(this.todayBtn !== false);
16816     
16817         this.updateNavArrows();
16818         this.fillMonths();
16819                                                 
16820         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16821         
16822         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16823          
16824         prevMonth.setUTCDate(day);
16825         
16826         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16827         
16828         var nextMonth = new Date(prevMonth);
16829         
16830         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16831         
16832         nextMonth = nextMonth.valueOf();
16833         
16834         var fillMonths = false;
16835         
16836         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16837         
16838         while(prevMonth.valueOf() < nextMonth) {
16839             var clsName = '';
16840             
16841             if (prevMonth.getUTCDay() === this.weekStart) {
16842                 if(fillMonths){
16843                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16844                 }
16845                     
16846                 fillMonths = {
16847                     tag: 'tr',
16848                     cn: []
16849                 };
16850                 
16851                 if(this.calendarWeeks){
16852                     // ISO 8601: First week contains first thursday.
16853                     // ISO also states week starts on Monday, but we can be more abstract here.
16854                     var
16855                     // Start of current week: based on weekstart/current date
16856                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16857                     // Thursday of this week
16858                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16859                     // First Thursday of year, year from thursday
16860                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16861                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16862                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16863                     
16864                     fillMonths.cn.push({
16865                         tag: 'td',
16866                         cls: 'cw',
16867                         html: calWeek
16868                     });
16869                 }
16870             }
16871             
16872             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16873                 clsName += ' old';
16874             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16875                 clsName += ' new';
16876             }
16877             if (this.todayHighlight &&
16878                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16879                 prevMonth.getUTCMonth() == today.getMonth() &&
16880                 prevMonth.getUTCDate() == today.getDate()) {
16881                 clsName += ' today';
16882             }
16883             
16884             if (currentDate && prevMonth.valueOf() === currentDate) {
16885                 clsName += ' active';
16886             }
16887             
16888             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16889                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16890                     clsName += ' disabled';
16891             }
16892             
16893             fillMonths.cn.push({
16894                 tag: 'td',
16895                 cls: 'day ' + clsName,
16896                 html: prevMonth.getDate()
16897             });
16898             
16899             prevMonth.setDate(prevMonth.getDate()+1);
16900         }
16901           
16902         var currentYear = this.date && this.date.getUTCFullYear();
16903         var currentMonth = this.date && this.date.getUTCMonth();
16904         
16905         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16906         
16907         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16908             v.removeClass('active');
16909             
16910             if(currentYear === year && k === currentMonth){
16911                 v.addClass('active');
16912             }
16913             
16914             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16915                 v.addClass('disabled');
16916             }
16917             
16918         });
16919         
16920         
16921         year = parseInt(year/10, 10) * 10;
16922         
16923         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16924         
16925         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16926         
16927         year -= 1;
16928         for (var i = -1; i < 11; i++) {
16929             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16930                 tag: 'span',
16931                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16932                 html: year
16933             });
16934             
16935             year += 1;
16936         }
16937     },
16938     
16939     showMode: function(dir) 
16940     {
16941         if (dir) {
16942             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16943         }
16944         
16945         Roo.each(this.picker().select('>div',true).elements, function(v){
16946             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16947             v.hide();
16948         });
16949         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16950     },
16951     
16952     place: function()
16953     {
16954         if(this.isInline) {
16955             return;
16956         }
16957         
16958         this.picker().removeClass(['bottom', 'top']);
16959         
16960         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16961             /*
16962              * place to the top of element!
16963              *
16964              */
16965             
16966             this.picker().addClass('top');
16967             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16968             
16969             return;
16970         }
16971         
16972         this.picker().addClass('bottom');
16973         
16974         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16975     },
16976     
16977     parseDate : function(value)
16978     {
16979         if(!value || value instanceof Date){
16980             return value;
16981         }
16982         var v = Date.parseDate(value, this.format);
16983         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16984             v = Date.parseDate(value, 'Y-m-d');
16985         }
16986         if(!v && this.altFormats){
16987             if(!this.altFormatsArray){
16988                 this.altFormatsArray = this.altFormats.split("|");
16989             }
16990             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16991                 v = Date.parseDate(value, this.altFormatsArray[i]);
16992             }
16993         }
16994         return v;
16995     },
16996     
16997     formatDate : function(date, fmt)
16998     {   
16999         return (!date || !(date instanceof Date)) ?
17000         date : date.dateFormat(fmt || this.format);
17001     },
17002     
17003     onFocus : function()
17004     {
17005         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17006         this.show();
17007     },
17008     
17009     onBlur : function()
17010     {
17011         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17012         
17013         var d = this.inputEl().getValue();
17014         
17015         this.setValue(d);
17016                 
17017         this.hide();
17018     },
17019     
17020     show : function()
17021     {
17022         this.picker().show();
17023         this.update();
17024         this.place();
17025         
17026         this.fireEvent('show', this, this.date);
17027     },
17028     
17029     hide : function()
17030     {
17031         if(this.isInline) {
17032             return;
17033         }
17034         this.picker().hide();
17035         this.viewMode = this.startViewMode;
17036         this.showMode();
17037         
17038         this.fireEvent('hide', this, this.date);
17039         
17040     },
17041     
17042     onMousedown: function(e)
17043     {
17044         e.stopPropagation();
17045         e.preventDefault();
17046     },
17047     
17048     keyup: function(e)
17049     {
17050         Roo.bootstrap.DateField.superclass.keyup.call(this);
17051         this.update();
17052     },
17053
17054     setValue: function(v)
17055     {
17056         if(this.fireEvent('beforeselect', this, v) !== false){
17057             var d = new Date(this.parseDate(v) ).clearTime();
17058         
17059             if(isNaN(d.getTime())){
17060                 this.date = this.viewDate = '';
17061                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17062                 return;
17063             }
17064
17065             v = this.formatDate(d);
17066
17067             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17068
17069             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17070
17071             this.update();
17072
17073             this.fireEvent('select', this, this.date);
17074         }
17075     },
17076     
17077     getValue: function()
17078     {
17079         return this.formatDate(this.date);
17080     },
17081     
17082     fireKey: function(e)
17083     {
17084         if (!this.picker().isVisible()){
17085             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17086                 this.show();
17087             }
17088             return;
17089         }
17090         
17091         var dateChanged = false,
17092         dir, day, month,
17093         newDate, newViewDate;
17094         
17095         switch(e.keyCode){
17096             case 27: // escape
17097                 this.hide();
17098                 e.preventDefault();
17099                 break;
17100             case 37: // left
17101             case 39: // right
17102                 if (!this.keyboardNavigation) {
17103                     break;
17104                 }
17105                 dir = e.keyCode == 37 ? -1 : 1;
17106                 
17107                 if (e.ctrlKey){
17108                     newDate = this.moveYear(this.date, dir);
17109                     newViewDate = this.moveYear(this.viewDate, dir);
17110                 } else if (e.shiftKey){
17111                     newDate = this.moveMonth(this.date, dir);
17112                     newViewDate = this.moveMonth(this.viewDate, dir);
17113                 } else {
17114                     newDate = new Date(this.date);
17115                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17116                     newViewDate = new Date(this.viewDate);
17117                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17118                 }
17119                 if (this.dateWithinRange(newDate)){
17120                     this.date = newDate;
17121                     this.viewDate = newViewDate;
17122                     this.setValue(this.formatDate(this.date));
17123 //                    this.update();
17124                     e.preventDefault();
17125                     dateChanged = true;
17126                 }
17127                 break;
17128             case 38: // up
17129             case 40: // down
17130                 if (!this.keyboardNavigation) {
17131                     break;
17132                 }
17133                 dir = e.keyCode == 38 ? -1 : 1;
17134                 if (e.ctrlKey){
17135                     newDate = this.moveYear(this.date, dir);
17136                     newViewDate = this.moveYear(this.viewDate, dir);
17137                 } else if (e.shiftKey){
17138                     newDate = this.moveMonth(this.date, dir);
17139                     newViewDate = this.moveMonth(this.viewDate, dir);
17140                 } else {
17141                     newDate = new Date(this.date);
17142                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17143                     newViewDate = new Date(this.viewDate);
17144                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17145                 }
17146                 if (this.dateWithinRange(newDate)){
17147                     this.date = newDate;
17148                     this.viewDate = newViewDate;
17149                     this.setValue(this.formatDate(this.date));
17150 //                    this.update();
17151                     e.preventDefault();
17152                     dateChanged = true;
17153                 }
17154                 break;
17155             case 13: // enter
17156                 this.setValue(this.formatDate(this.date));
17157                 this.hide();
17158                 e.preventDefault();
17159                 break;
17160             case 9: // tab
17161                 this.setValue(this.formatDate(this.date));
17162                 this.hide();
17163                 break;
17164             case 16: // shift
17165             case 17: // ctrl
17166             case 18: // alt
17167                 break;
17168             default :
17169                 this.hide();
17170                 
17171         }
17172     },
17173     
17174     
17175     onClick: function(e) 
17176     {
17177         e.stopPropagation();
17178         e.preventDefault();
17179         
17180         var target = e.getTarget();
17181         
17182         if(target.nodeName.toLowerCase() === 'i'){
17183             target = Roo.get(target).dom.parentNode;
17184         }
17185         
17186         var nodeName = target.nodeName;
17187         var className = target.className;
17188         var html = target.innerHTML;
17189         //Roo.log(nodeName);
17190         
17191         switch(nodeName.toLowerCase()) {
17192             case 'th':
17193                 switch(className) {
17194                     case 'switch':
17195                         this.showMode(1);
17196                         break;
17197                     case 'prev':
17198                     case 'next':
17199                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17200                         switch(this.viewMode){
17201                                 case 0:
17202                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17203                                         break;
17204                                 case 1:
17205                                 case 2:
17206                                         this.viewDate = this.moveYear(this.viewDate, dir);
17207                                         break;
17208                         }
17209                         this.fill();
17210                         break;
17211                     case 'today':
17212                         var date = new Date();
17213                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17214 //                        this.fill()
17215                         this.setValue(this.formatDate(this.date));
17216                         
17217                         this.hide();
17218                         break;
17219                 }
17220                 break;
17221             case 'span':
17222                 if (className.indexOf('disabled') < 0) {
17223                     this.viewDate.setUTCDate(1);
17224                     if (className.indexOf('month') > -1) {
17225                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17226                     } else {
17227                         var year = parseInt(html, 10) || 0;
17228                         this.viewDate.setUTCFullYear(year);
17229                         
17230                     }
17231                     
17232                     if(this.singleMode){
17233                         this.setValue(this.formatDate(this.viewDate));
17234                         this.hide();
17235                         return;
17236                     }
17237                     
17238                     this.showMode(-1);
17239                     this.fill();
17240                 }
17241                 break;
17242                 
17243             case 'td':
17244                 //Roo.log(className);
17245                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17246                     var day = parseInt(html, 10) || 1;
17247                     var year = this.viewDate.getUTCFullYear(),
17248                         month = this.viewDate.getUTCMonth();
17249
17250                     if (className.indexOf('old') > -1) {
17251                         if(month === 0 ){
17252                             month = 11;
17253                             year -= 1;
17254                         }else{
17255                             month -= 1;
17256                         }
17257                     } else if (className.indexOf('new') > -1) {
17258                         if (month == 11) {
17259                             month = 0;
17260                             year += 1;
17261                         } else {
17262                             month += 1;
17263                         }
17264                     }
17265                     //Roo.log([year,month,day]);
17266                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17267                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17268 //                    this.fill();
17269                     //Roo.log(this.formatDate(this.date));
17270                     this.setValue(this.formatDate(this.date));
17271                     this.hide();
17272                 }
17273                 break;
17274         }
17275     },
17276     
17277     setStartDate: function(startDate)
17278     {
17279         this.startDate = startDate || -Infinity;
17280         if (this.startDate !== -Infinity) {
17281             this.startDate = this.parseDate(this.startDate);
17282         }
17283         this.update();
17284         this.updateNavArrows();
17285     },
17286
17287     setEndDate: function(endDate)
17288     {
17289         this.endDate = endDate || Infinity;
17290         if (this.endDate !== Infinity) {
17291             this.endDate = this.parseDate(this.endDate);
17292         }
17293         this.update();
17294         this.updateNavArrows();
17295     },
17296     
17297     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17298     {
17299         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17300         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17301             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17302         }
17303         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17304             return parseInt(d, 10);
17305         });
17306         this.update();
17307         this.updateNavArrows();
17308     },
17309     
17310     updateNavArrows: function() 
17311     {
17312         if(this.singleMode){
17313             return;
17314         }
17315         
17316         var d = new Date(this.viewDate),
17317         year = d.getUTCFullYear(),
17318         month = d.getUTCMonth();
17319         
17320         Roo.each(this.picker().select('.prev', true).elements, function(v){
17321             v.show();
17322             switch (this.viewMode) {
17323                 case 0:
17324
17325                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17326                         v.hide();
17327                     }
17328                     break;
17329                 case 1:
17330                 case 2:
17331                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17332                         v.hide();
17333                     }
17334                     break;
17335             }
17336         });
17337         
17338         Roo.each(this.picker().select('.next', true).elements, function(v){
17339             v.show();
17340             switch (this.viewMode) {
17341                 case 0:
17342
17343                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17344                         v.hide();
17345                     }
17346                     break;
17347                 case 1:
17348                 case 2:
17349                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17350                         v.hide();
17351                     }
17352                     break;
17353             }
17354         })
17355     },
17356     
17357     moveMonth: function(date, dir)
17358     {
17359         if (!dir) {
17360             return date;
17361         }
17362         var new_date = new Date(date.valueOf()),
17363         day = new_date.getUTCDate(),
17364         month = new_date.getUTCMonth(),
17365         mag = Math.abs(dir),
17366         new_month, test;
17367         dir = dir > 0 ? 1 : -1;
17368         if (mag == 1){
17369             test = dir == -1
17370             // If going back one month, make sure month is not current month
17371             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17372             ? function(){
17373                 return new_date.getUTCMonth() == month;
17374             }
17375             // If going forward one month, make sure month is as expected
17376             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17377             : function(){
17378                 return new_date.getUTCMonth() != new_month;
17379             };
17380             new_month = month + dir;
17381             new_date.setUTCMonth(new_month);
17382             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17383             if (new_month < 0 || new_month > 11) {
17384                 new_month = (new_month + 12) % 12;
17385             }
17386         } else {
17387             // For magnitudes >1, move one month at a time...
17388             for (var i=0; i<mag; i++) {
17389                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17390                 new_date = this.moveMonth(new_date, dir);
17391             }
17392             // ...then reset the day, keeping it in the new month
17393             new_month = new_date.getUTCMonth();
17394             new_date.setUTCDate(day);
17395             test = function(){
17396                 return new_month != new_date.getUTCMonth();
17397             };
17398         }
17399         // Common date-resetting loop -- if date is beyond end of month, make it
17400         // end of month
17401         while (test()){
17402             new_date.setUTCDate(--day);
17403             new_date.setUTCMonth(new_month);
17404         }
17405         return new_date;
17406     },
17407
17408     moveYear: function(date, dir)
17409     {
17410         return this.moveMonth(date, dir*12);
17411     },
17412
17413     dateWithinRange: function(date)
17414     {
17415         return date >= this.startDate && date <= this.endDate;
17416     },
17417
17418     
17419     remove: function() 
17420     {
17421         this.picker().remove();
17422     }
17423    
17424 });
17425
17426 Roo.apply(Roo.bootstrap.DateField,  {
17427     
17428     head : {
17429         tag: 'thead',
17430         cn: [
17431         {
17432             tag: 'tr',
17433             cn: [
17434             {
17435                 tag: 'th',
17436                 cls: 'prev',
17437                 html: '<i class="fa fa-arrow-left"/>'
17438             },
17439             {
17440                 tag: 'th',
17441                 cls: 'switch',
17442                 colspan: '5'
17443             },
17444             {
17445                 tag: 'th',
17446                 cls: 'next',
17447                 html: '<i class="fa fa-arrow-right"/>'
17448             }
17449
17450             ]
17451         }
17452         ]
17453     },
17454     
17455     content : {
17456         tag: 'tbody',
17457         cn: [
17458         {
17459             tag: 'tr',
17460             cn: [
17461             {
17462                 tag: 'td',
17463                 colspan: '7'
17464             }
17465             ]
17466         }
17467         ]
17468     },
17469     
17470     footer : {
17471         tag: 'tfoot',
17472         cn: [
17473         {
17474             tag: 'tr',
17475             cn: [
17476             {
17477                 tag: 'th',
17478                 colspan: '7',
17479                 cls: 'today'
17480             }
17481                     
17482             ]
17483         }
17484         ]
17485     },
17486     
17487     dates:{
17488         en: {
17489             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17490             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17491             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17492             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17493             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17494             today: "Today"
17495         }
17496     },
17497     
17498     modes: [
17499     {
17500         clsName: 'days',
17501         navFnc: 'Month',
17502         navStep: 1
17503     },
17504     {
17505         clsName: 'months',
17506         navFnc: 'FullYear',
17507         navStep: 1
17508     },
17509     {
17510         clsName: 'years',
17511         navFnc: 'FullYear',
17512         navStep: 10
17513     }]
17514 });
17515
17516 Roo.apply(Roo.bootstrap.DateField,  {
17517   
17518     template : {
17519         tag: 'div',
17520         cls: 'datepicker dropdown-menu roo-dynamic',
17521         cn: [
17522         {
17523             tag: 'div',
17524             cls: 'datepicker-days',
17525             cn: [
17526             {
17527                 tag: 'table',
17528                 cls: 'table-condensed',
17529                 cn:[
17530                 Roo.bootstrap.DateField.head,
17531                 {
17532                     tag: 'tbody'
17533                 },
17534                 Roo.bootstrap.DateField.footer
17535                 ]
17536             }
17537             ]
17538         },
17539         {
17540             tag: 'div',
17541             cls: 'datepicker-months',
17542             cn: [
17543             {
17544                 tag: 'table',
17545                 cls: 'table-condensed',
17546                 cn:[
17547                 Roo.bootstrap.DateField.head,
17548                 Roo.bootstrap.DateField.content,
17549                 Roo.bootstrap.DateField.footer
17550                 ]
17551             }
17552             ]
17553         },
17554         {
17555             tag: 'div',
17556             cls: 'datepicker-years',
17557             cn: [
17558             {
17559                 tag: 'table',
17560                 cls: 'table-condensed',
17561                 cn:[
17562                 Roo.bootstrap.DateField.head,
17563                 Roo.bootstrap.DateField.content,
17564                 Roo.bootstrap.DateField.footer
17565                 ]
17566             }
17567             ]
17568         }
17569         ]
17570     }
17571 });
17572
17573  
17574
17575  /*
17576  * - LGPL
17577  *
17578  * TimeField
17579  * 
17580  */
17581
17582 /**
17583  * @class Roo.bootstrap.TimeField
17584  * @extends Roo.bootstrap.Input
17585  * Bootstrap DateField class
17586  * 
17587  * 
17588  * @constructor
17589  * Create a new TimeField
17590  * @param {Object} config The config object
17591  */
17592
17593 Roo.bootstrap.TimeField = function(config){
17594     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17595     this.addEvents({
17596             /**
17597              * @event show
17598              * Fires when this field show.
17599              * @param {Roo.bootstrap.DateField} thisthis
17600              * @param {Mixed} date The date value
17601              */
17602             show : true,
17603             /**
17604              * @event show
17605              * Fires when this field hide.
17606              * @param {Roo.bootstrap.DateField} this
17607              * @param {Mixed} date The date value
17608              */
17609             hide : true,
17610             /**
17611              * @event select
17612              * Fires when select a date.
17613              * @param {Roo.bootstrap.DateField} this
17614              * @param {Mixed} date The date value
17615              */
17616             select : true
17617         });
17618 };
17619
17620 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17621     
17622     /**
17623      * @cfg {String} format
17624      * The default time format string which can be overriden for localization support.  The format must be
17625      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17626      */
17627     format : "H:i",
17628        
17629     onRender: function(ct, position)
17630     {
17631         
17632         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17633                 
17634         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17635         
17636         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17637         
17638         this.pop = this.picker().select('>.datepicker-time',true).first();
17639         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17640         
17641         this.picker().on('mousedown', this.onMousedown, this);
17642         this.picker().on('click', this.onClick, this);
17643         
17644         this.picker().addClass('datepicker-dropdown');
17645     
17646         this.fillTime();
17647         this.update();
17648             
17649         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17650         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17651         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17652         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17653         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17654         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17655
17656     },
17657     
17658     fireKey: function(e){
17659         if (!this.picker().isVisible()){
17660             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17661                 this.show();
17662             }
17663             return;
17664         }
17665
17666         e.preventDefault();
17667         
17668         switch(e.keyCode){
17669             case 27: // escape
17670                 this.hide();
17671                 break;
17672             case 37: // left
17673             case 39: // right
17674                 this.onTogglePeriod();
17675                 break;
17676             case 38: // up
17677                 this.onIncrementMinutes();
17678                 break;
17679             case 40: // down
17680                 this.onDecrementMinutes();
17681                 break;
17682             case 13: // enter
17683             case 9: // tab
17684                 this.setTime();
17685                 break;
17686         }
17687     },
17688     
17689     onClick: function(e) {
17690         e.stopPropagation();
17691         e.preventDefault();
17692     },
17693     
17694     picker : function()
17695     {
17696         return this.el.select('.datepicker', true).first();
17697     },
17698     
17699     fillTime: function()
17700     {    
17701         var time = this.pop.select('tbody', true).first();
17702         
17703         time.dom.innerHTML = '';
17704         
17705         time.createChild({
17706             tag: 'tr',
17707             cn: [
17708                 {
17709                     tag: 'td',
17710                     cn: [
17711                         {
17712                             tag: 'a',
17713                             href: '#',
17714                             cls: 'btn',
17715                             cn: [
17716                                 {
17717                                     tag: 'span',
17718                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17719                                 }
17720                             ]
17721                         } 
17722                     ]
17723                 },
17724                 {
17725                     tag: 'td',
17726                     cls: 'separator'
17727                 },
17728                 {
17729                     tag: 'td',
17730                     cn: [
17731                         {
17732                             tag: 'a',
17733                             href: '#',
17734                             cls: 'btn',
17735                             cn: [
17736                                 {
17737                                     tag: 'span',
17738                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17739                                 }
17740                             ]
17741                         }
17742                     ]
17743                 },
17744                 {
17745                     tag: 'td',
17746                     cls: 'separator'
17747                 }
17748             ]
17749         });
17750         
17751         time.createChild({
17752             tag: 'tr',
17753             cn: [
17754                 {
17755                     tag: 'td',
17756                     cn: [
17757                         {
17758                             tag: 'span',
17759                             cls: 'timepicker-hour',
17760                             html: '00'
17761                         }  
17762                     ]
17763                 },
17764                 {
17765                     tag: 'td',
17766                     cls: 'separator',
17767                     html: ':'
17768                 },
17769                 {
17770                     tag: 'td',
17771                     cn: [
17772                         {
17773                             tag: 'span',
17774                             cls: 'timepicker-minute',
17775                             html: '00'
17776                         }  
17777                     ]
17778                 },
17779                 {
17780                     tag: 'td',
17781                     cls: 'separator'
17782                 },
17783                 {
17784                     tag: 'td',
17785                     cn: [
17786                         {
17787                             tag: 'button',
17788                             type: 'button',
17789                             cls: 'btn btn-primary period',
17790                             html: 'AM'
17791                             
17792                         }
17793                     ]
17794                 }
17795             ]
17796         });
17797         
17798         time.createChild({
17799             tag: 'tr',
17800             cn: [
17801                 {
17802                     tag: 'td',
17803                     cn: [
17804                         {
17805                             tag: 'a',
17806                             href: '#',
17807                             cls: 'btn',
17808                             cn: [
17809                                 {
17810                                     tag: 'span',
17811                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17812                                 }
17813                             ]
17814                         }
17815                     ]
17816                 },
17817                 {
17818                     tag: 'td',
17819                     cls: 'separator'
17820                 },
17821                 {
17822                     tag: 'td',
17823                     cn: [
17824                         {
17825                             tag: 'a',
17826                             href: '#',
17827                             cls: 'btn',
17828                             cn: [
17829                                 {
17830                                     tag: 'span',
17831                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17832                                 }
17833                             ]
17834                         }
17835                     ]
17836                 },
17837                 {
17838                     tag: 'td',
17839                     cls: 'separator'
17840                 }
17841             ]
17842         });
17843         
17844     },
17845     
17846     update: function()
17847     {
17848         
17849         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17850         
17851         this.fill();
17852     },
17853     
17854     fill: function() 
17855     {
17856         var hours = this.time.getHours();
17857         var minutes = this.time.getMinutes();
17858         var period = 'AM';
17859         
17860         if(hours > 11){
17861             period = 'PM';
17862         }
17863         
17864         if(hours == 0){
17865             hours = 12;
17866         }
17867         
17868         
17869         if(hours > 12){
17870             hours = hours - 12;
17871         }
17872         
17873         if(hours < 10){
17874             hours = '0' + hours;
17875         }
17876         
17877         if(minutes < 10){
17878             minutes = '0' + minutes;
17879         }
17880         
17881         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17882         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17883         this.pop.select('button', true).first().dom.innerHTML = period;
17884         
17885     },
17886     
17887     place: function()
17888     {   
17889         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17890         
17891         var cls = ['bottom'];
17892         
17893         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17894             cls.pop();
17895             cls.push('top');
17896         }
17897         
17898         cls.push('right');
17899         
17900         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17901             cls.pop();
17902             cls.push('left');
17903         }
17904         
17905         this.picker().addClass(cls.join('-'));
17906         
17907         var _this = this;
17908         
17909         Roo.each(cls, function(c){
17910             if(c == 'bottom'){
17911                 _this.picker().setTop(_this.inputEl().getHeight());
17912                 return;
17913             }
17914             if(c == 'top'){
17915                 _this.picker().setTop(0 - _this.picker().getHeight());
17916                 return;
17917             }
17918             
17919             if(c == 'left'){
17920                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17921                 return;
17922             }
17923             if(c == 'right'){
17924                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17925                 return;
17926             }
17927         });
17928         
17929     },
17930   
17931     onFocus : function()
17932     {
17933         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17934         this.show();
17935     },
17936     
17937     onBlur : function()
17938     {
17939         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17940         this.hide();
17941     },
17942     
17943     show : function()
17944     {
17945         this.picker().show();
17946         this.pop.show();
17947         this.update();
17948         this.place();
17949         
17950         this.fireEvent('show', this, this.date);
17951     },
17952     
17953     hide : function()
17954     {
17955         this.picker().hide();
17956         this.pop.hide();
17957         
17958         this.fireEvent('hide', this, this.date);
17959     },
17960     
17961     setTime : function()
17962     {
17963         this.hide();
17964         this.setValue(this.time.format(this.format));
17965         
17966         this.fireEvent('select', this, this.date);
17967         
17968         
17969     },
17970     
17971     onMousedown: function(e){
17972         e.stopPropagation();
17973         e.preventDefault();
17974     },
17975     
17976     onIncrementHours: function()
17977     {
17978         Roo.log('onIncrementHours');
17979         this.time = this.time.add(Date.HOUR, 1);
17980         this.update();
17981         
17982     },
17983     
17984     onDecrementHours: function()
17985     {
17986         Roo.log('onDecrementHours');
17987         this.time = this.time.add(Date.HOUR, -1);
17988         this.update();
17989     },
17990     
17991     onIncrementMinutes: function()
17992     {
17993         Roo.log('onIncrementMinutes');
17994         this.time = this.time.add(Date.MINUTE, 1);
17995         this.update();
17996     },
17997     
17998     onDecrementMinutes: function()
17999     {
18000         Roo.log('onDecrementMinutes');
18001         this.time = this.time.add(Date.MINUTE, -1);
18002         this.update();
18003     },
18004     
18005     onTogglePeriod: function()
18006     {
18007         Roo.log('onTogglePeriod');
18008         this.time = this.time.add(Date.HOUR, 12);
18009         this.update();
18010     }
18011     
18012    
18013 });
18014
18015 Roo.apply(Roo.bootstrap.TimeField,  {
18016     
18017     content : {
18018         tag: 'tbody',
18019         cn: [
18020             {
18021                 tag: 'tr',
18022                 cn: [
18023                 {
18024                     tag: 'td',
18025                     colspan: '7'
18026                 }
18027                 ]
18028             }
18029         ]
18030     },
18031     
18032     footer : {
18033         tag: 'tfoot',
18034         cn: [
18035             {
18036                 tag: 'tr',
18037                 cn: [
18038                 {
18039                     tag: 'th',
18040                     colspan: '7',
18041                     cls: '',
18042                     cn: [
18043                         {
18044                             tag: 'button',
18045                             cls: 'btn btn-info ok',
18046                             html: 'OK'
18047                         }
18048                     ]
18049                 }
18050
18051                 ]
18052             }
18053         ]
18054     }
18055 });
18056
18057 Roo.apply(Roo.bootstrap.TimeField,  {
18058   
18059     template : {
18060         tag: 'div',
18061         cls: 'datepicker dropdown-menu',
18062         cn: [
18063             {
18064                 tag: 'div',
18065                 cls: 'datepicker-time',
18066                 cn: [
18067                 {
18068                     tag: 'table',
18069                     cls: 'table-condensed',
18070                     cn:[
18071                     Roo.bootstrap.TimeField.content,
18072                     Roo.bootstrap.TimeField.footer
18073                     ]
18074                 }
18075                 ]
18076             }
18077         ]
18078     }
18079 });
18080
18081  
18082
18083  /*
18084  * - LGPL
18085  *
18086  * MonthField
18087  * 
18088  */
18089
18090 /**
18091  * @class Roo.bootstrap.MonthField
18092  * @extends Roo.bootstrap.Input
18093  * Bootstrap MonthField class
18094  * 
18095  * @cfg {String} language default en
18096  * 
18097  * @constructor
18098  * Create a new MonthField
18099  * @param {Object} config The config object
18100  */
18101
18102 Roo.bootstrap.MonthField = function(config){
18103     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18104     
18105     this.addEvents({
18106         /**
18107          * @event show
18108          * Fires when this field show.
18109          * @param {Roo.bootstrap.MonthField} this
18110          * @param {Mixed} date The date value
18111          */
18112         show : true,
18113         /**
18114          * @event show
18115          * Fires when this field hide.
18116          * @param {Roo.bootstrap.MonthField} this
18117          * @param {Mixed} date The date value
18118          */
18119         hide : true,
18120         /**
18121          * @event select
18122          * Fires when select a date.
18123          * @param {Roo.bootstrap.MonthField} this
18124          * @param {String} oldvalue The old value
18125          * @param {String} newvalue The new value
18126          */
18127         select : true
18128     });
18129 };
18130
18131 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18132     
18133     onRender: function(ct, position)
18134     {
18135         
18136         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18137         
18138         this.language = this.language || 'en';
18139         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18140         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18141         
18142         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18143         this.isInline = false;
18144         this.isInput = true;
18145         this.component = this.el.select('.add-on', true).first() || false;
18146         this.component = (this.component && this.component.length === 0) ? false : this.component;
18147         this.hasInput = this.component && this.inputEL().length;
18148         
18149         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18150         
18151         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18152         
18153         this.picker().on('mousedown', this.onMousedown, this);
18154         this.picker().on('click', this.onClick, this);
18155         
18156         this.picker().addClass('datepicker-dropdown');
18157         
18158         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18159             v.setStyle('width', '189px');
18160         });
18161         
18162         this.fillMonths();
18163         
18164         this.update();
18165         
18166         if(this.isInline) {
18167             this.show();
18168         }
18169         
18170     },
18171     
18172     setValue: function(v, suppressEvent)
18173     {   
18174         var o = this.getValue();
18175         
18176         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18177         
18178         this.update();
18179
18180         if(suppressEvent !== true){
18181             this.fireEvent('select', this, o, v);
18182         }
18183         
18184     },
18185     
18186     getValue: function()
18187     {
18188         return this.value;
18189     },
18190     
18191     onClick: function(e) 
18192     {
18193         e.stopPropagation();
18194         e.preventDefault();
18195         
18196         var target = e.getTarget();
18197         
18198         if(target.nodeName.toLowerCase() === 'i'){
18199             target = Roo.get(target).dom.parentNode;
18200         }
18201         
18202         var nodeName = target.nodeName;
18203         var className = target.className;
18204         var html = target.innerHTML;
18205         
18206         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18207             return;
18208         }
18209         
18210         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18211         
18212         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18213         
18214         this.hide();
18215                         
18216     },
18217     
18218     picker : function()
18219     {
18220         return this.pickerEl;
18221     },
18222     
18223     fillMonths: function()
18224     {    
18225         var i = 0;
18226         var months = this.picker().select('>.datepicker-months td', true).first();
18227         
18228         months.dom.innerHTML = '';
18229         
18230         while (i < 12) {
18231             var month = {
18232                 tag: 'span',
18233                 cls: 'month',
18234                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18235             };
18236             
18237             months.createChild(month);
18238         }
18239         
18240     },
18241     
18242     update: function()
18243     {
18244         var _this = this;
18245         
18246         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18247             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18248         }
18249         
18250         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18251             e.removeClass('active');
18252             
18253             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18254                 e.addClass('active');
18255             }
18256         })
18257     },
18258     
18259     place: function()
18260     {
18261         if(this.isInline) {
18262             return;
18263         }
18264         
18265         this.picker().removeClass(['bottom', 'top']);
18266         
18267         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18268             /*
18269              * place to the top of element!
18270              *
18271              */
18272             
18273             this.picker().addClass('top');
18274             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18275             
18276             return;
18277         }
18278         
18279         this.picker().addClass('bottom');
18280         
18281         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18282     },
18283     
18284     onFocus : function()
18285     {
18286         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18287         this.show();
18288     },
18289     
18290     onBlur : function()
18291     {
18292         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18293         
18294         var d = this.inputEl().getValue();
18295         
18296         this.setValue(d);
18297                 
18298         this.hide();
18299     },
18300     
18301     show : function()
18302     {
18303         this.picker().show();
18304         this.picker().select('>.datepicker-months', true).first().show();
18305         this.update();
18306         this.place();
18307         
18308         this.fireEvent('show', this, this.date);
18309     },
18310     
18311     hide : function()
18312     {
18313         if(this.isInline) {
18314             return;
18315         }
18316         this.picker().hide();
18317         this.fireEvent('hide', this, this.date);
18318         
18319     },
18320     
18321     onMousedown: function(e)
18322     {
18323         e.stopPropagation();
18324         e.preventDefault();
18325     },
18326     
18327     keyup: function(e)
18328     {
18329         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18330         this.update();
18331     },
18332
18333     fireKey: function(e)
18334     {
18335         if (!this.picker().isVisible()){
18336             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18337                 this.show();
18338             }
18339             return;
18340         }
18341         
18342         var dir;
18343         
18344         switch(e.keyCode){
18345             case 27: // escape
18346                 this.hide();
18347                 e.preventDefault();
18348                 break;
18349             case 37: // left
18350             case 39: // right
18351                 dir = e.keyCode == 37 ? -1 : 1;
18352                 
18353                 this.vIndex = this.vIndex + dir;
18354                 
18355                 if(this.vIndex < 0){
18356                     this.vIndex = 0;
18357                 }
18358                 
18359                 if(this.vIndex > 11){
18360                     this.vIndex = 11;
18361                 }
18362                 
18363                 if(isNaN(this.vIndex)){
18364                     this.vIndex = 0;
18365                 }
18366                 
18367                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18368                 
18369                 break;
18370             case 38: // up
18371             case 40: // down
18372                 
18373                 dir = e.keyCode == 38 ? -1 : 1;
18374                 
18375                 this.vIndex = this.vIndex + dir * 4;
18376                 
18377                 if(this.vIndex < 0){
18378                     this.vIndex = 0;
18379                 }
18380                 
18381                 if(this.vIndex > 11){
18382                     this.vIndex = 11;
18383                 }
18384                 
18385                 if(isNaN(this.vIndex)){
18386                     this.vIndex = 0;
18387                 }
18388                 
18389                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18390                 break;
18391                 
18392             case 13: // enter
18393                 
18394                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18395                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18396                 }
18397                 
18398                 this.hide();
18399                 e.preventDefault();
18400                 break;
18401             case 9: // tab
18402                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18403                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18404                 }
18405                 this.hide();
18406                 break;
18407             case 16: // shift
18408             case 17: // ctrl
18409             case 18: // alt
18410                 break;
18411             default :
18412                 this.hide();
18413                 
18414         }
18415     },
18416     
18417     remove: function() 
18418     {
18419         this.picker().remove();
18420     }
18421    
18422 });
18423
18424 Roo.apply(Roo.bootstrap.MonthField,  {
18425     
18426     content : {
18427         tag: 'tbody',
18428         cn: [
18429         {
18430             tag: 'tr',
18431             cn: [
18432             {
18433                 tag: 'td',
18434                 colspan: '7'
18435             }
18436             ]
18437         }
18438         ]
18439     },
18440     
18441     dates:{
18442         en: {
18443             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18444             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18445         }
18446     }
18447 });
18448
18449 Roo.apply(Roo.bootstrap.MonthField,  {
18450   
18451     template : {
18452         tag: 'div',
18453         cls: 'datepicker dropdown-menu roo-dynamic',
18454         cn: [
18455             {
18456                 tag: 'div',
18457                 cls: 'datepicker-months',
18458                 cn: [
18459                 {
18460                     tag: 'table',
18461                     cls: 'table-condensed',
18462                     cn:[
18463                         Roo.bootstrap.DateField.content
18464                     ]
18465                 }
18466                 ]
18467             }
18468         ]
18469     }
18470 });
18471
18472  
18473
18474  
18475  /*
18476  * - LGPL
18477  *
18478  * CheckBox
18479  * 
18480  */
18481
18482 /**
18483  * @class Roo.bootstrap.CheckBox
18484  * @extends Roo.bootstrap.Input
18485  * Bootstrap CheckBox class
18486  * 
18487  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18488  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18489  * @cfg {String} boxLabel The text that appears beside the checkbox
18490  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18491  * @cfg {Boolean} checked initnal the element
18492  * @cfg {Boolean} inline inline the element (default false)
18493  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18494  * 
18495  * @constructor
18496  * Create a new CheckBox
18497  * @param {Object} config The config object
18498  */
18499
18500 Roo.bootstrap.CheckBox = function(config){
18501     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18502    
18503     this.addEvents({
18504         /**
18505         * @event check
18506         * Fires when the element is checked or unchecked.
18507         * @param {Roo.bootstrap.CheckBox} this This input
18508         * @param {Boolean} checked The new checked value
18509         */
18510        check : true
18511     });
18512     
18513 };
18514
18515 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18516   
18517     inputType: 'checkbox',
18518     inputValue: 1,
18519     valueOff: 0,
18520     boxLabel: false,
18521     checked: false,
18522     weight : false,
18523     inline: false,
18524     
18525     getAutoCreate : function()
18526     {
18527         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18528         
18529         var id = Roo.id();
18530         
18531         var cfg = {};
18532         
18533         cfg.cls = 'form-group ' + this.inputType; //input-group
18534         
18535         if(this.inline){
18536             cfg.cls += ' ' + this.inputType + '-inline';
18537         }
18538         
18539         var input =  {
18540             tag: 'input',
18541             id : id,
18542             type : this.inputType,
18543             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18544             cls : 'roo-' + this.inputType, //'form-box',
18545             placeholder : this.placeholder || ''
18546             
18547         };
18548         
18549         if (this.weight) { // Validity check?
18550             cfg.cls += " " + this.inputType + "-" + this.weight;
18551         }
18552         
18553         if (this.disabled) {
18554             input.disabled=true;
18555         }
18556         
18557         if(this.checked){
18558             input.checked = this.checked;
18559         }
18560         
18561         if (this.name) {
18562             input.name = this.name;
18563         }
18564         
18565         if (this.size) {
18566             input.cls += ' input-' + this.size;
18567         }
18568         
18569         var settings=this;
18570         
18571         ['xs','sm','md','lg'].map(function(size){
18572             if (settings[size]) {
18573                 cfg.cls += ' col-' + size + '-' + settings[size];
18574             }
18575         });
18576         
18577         var inputblock = input;
18578          
18579         if (this.before || this.after) {
18580             
18581             inputblock = {
18582                 cls : 'input-group',
18583                 cn :  [] 
18584             };
18585             
18586             if (this.before) {
18587                 inputblock.cn.push({
18588                     tag :'span',
18589                     cls : 'input-group-addon',
18590                     html : this.before
18591                 });
18592             }
18593             
18594             inputblock.cn.push(input);
18595             
18596             if (this.after) {
18597                 inputblock.cn.push({
18598                     tag :'span',
18599                     cls : 'input-group-addon',
18600                     html : this.after
18601                 });
18602             }
18603             
18604         }
18605         
18606         if (align ==='left' && this.fieldLabel.length) {
18607                 Roo.log("left and has label");
18608                 cfg.cn = [
18609                     
18610                     {
18611                         tag: 'label',
18612                         'for' :  id,
18613                         cls : 'control-label col-md-' + this.labelWidth,
18614                         html : this.fieldLabel
18615                         
18616                     },
18617                     {
18618                         cls : "col-md-" + (12 - this.labelWidth), 
18619                         cn: [
18620                             inputblock
18621                         ]
18622                     }
18623                     
18624                 ];
18625         } else if ( this.fieldLabel.length) {
18626                 Roo.log(" label");
18627                 cfg.cn = [
18628                    
18629                     {
18630                         tag: this.boxLabel ? 'span' : 'label',
18631                         'for': id,
18632                         cls: 'control-label box-input-label',
18633                         //cls : 'input-group-addon',
18634                         html : this.fieldLabel
18635                         
18636                     },
18637                     
18638                     inputblock
18639                     
18640                 ];
18641
18642         } else {
18643             
18644                 Roo.log(" no label && no align");
18645                 cfg.cn = [  inputblock ] ;
18646                 
18647                 
18648         }
18649         if(this.boxLabel){
18650              var boxLabelCfg = {
18651                 tag: 'label',
18652                 //'for': id, // box label is handled by onclick - so no for...
18653                 cls: 'box-label',
18654                 html: this.boxLabel
18655             };
18656             
18657             if(this.tooltip){
18658                 boxLabelCfg.tooltip = this.tooltip;
18659             }
18660              
18661             cfg.cn.push(boxLabelCfg);
18662         }
18663         
18664         
18665        
18666         return cfg;
18667         
18668     },
18669     
18670     /**
18671      * return the real input element.
18672      */
18673     inputEl: function ()
18674     {
18675         return this.el.select('input.roo-' + this.inputType,true).first();
18676     },
18677     
18678     labelEl: function()
18679     {
18680         return this.el.select('label.control-label',true).first();
18681     },
18682     /* depricated... */
18683     
18684     label: function()
18685     {
18686         return this.labelEl();
18687     },
18688     
18689     initEvents : function()
18690     {
18691 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18692         
18693         this.inputEl().on('click', this.onClick,  this);
18694         
18695         if (this.boxLabel) { 
18696             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18697         }
18698         
18699         this.startValue = this.getValue();
18700         
18701         if(this.groupId){
18702             Roo.bootstrap.CheckBox.register(this);
18703         }
18704     },
18705     
18706     onClick : function()
18707     {   
18708         this.setChecked(!this.checked);
18709     },
18710     
18711     setChecked : function(state,suppressEvent)
18712     {
18713         this.startValue = this.getValue();
18714         
18715         if(this.inputType == 'radio'){
18716             
18717             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18718                 e.dom.checked = false;
18719             });
18720             
18721             this.inputEl().dom.checked = true;
18722             
18723             this.inputEl().dom.value = this.inputValue;
18724             
18725             if(suppressEvent !== true){
18726                 this.fireEvent('check', this, true);
18727             }
18728             
18729             this.validate();
18730             
18731             return;
18732         }
18733         
18734         this.checked = state;
18735         
18736         this.inputEl().dom.checked = state;
18737         
18738         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18739         
18740         if(suppressEvent !== true){
18741             this.fireEvent('check', this, state);
18742         }
18743         
18744         this.validate();
18745     },
18746     
18747     getValue : function()
18748     {
18749         if(this.inputType == 'radio'){
18750             return this.getGroupValue();
18751         }
18752         
18753         return this.inputEl().getValue();
18754         
18755     },
18756     
18757     getGroupValue : function()
18758     {
18759         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18760             return '';
18761         }
18762         
18763         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18764     },
18765     
18766     setValue : function(v,suppressEvent)
18767     {
18768         if(this.inputType == 'radio'){
18769             this.setGroupValue(v, suppressEvent);
18770             return;
18771         }
18772         
18773         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18774         
18775         this.validate();
18776     },
18777     
18778     setGroupValue : function(v, suppressEvent)
18779     {
18780         this.startValue = this.getValue();
18781         
18782         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18783             e.dom.checked = false;
18784             
18785             if(e.dom.value == v){
18786                 e.dom.checked = true;
18787             }
18788         });
18789         
18790         if(suppressEvent !== true){
18791             this.fireEvent('check', this, true);
18792         }
18793
18794         this.validate();
18795         
18796         return;
18797     },
18798     
18799     validate : function()
18800     {
18801         if(
18802                 this.disabled || 
18803                 (this.inputType == 'radio' && this.validateRadio()) ||
18804                 (this.inputType == 'checkbox' && this.validateCheckbox())
18805         ){
18806             this.markValid();
18807             return true;
18808         }
18809         
18810         this.markInvalid();
18811         return false;
18812     },
18813     
18814     validateRadio : function()
18815     {
18816         var valid = false;
18817         
18818         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18819             if(!e.dom.checked){
18820                 return;
18821             }
18822             
18823             valid = true;
18824             
18825             return false;
18826         });
18827         
18828         return valid;
18829     },
18830     
18831     validateCheckbox : function()
18832     {
18833         if(!this.groupId){
18834             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18835         }
18836         
18837         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18838         
18839         if(!group){
18840             return false;
18841         }
18842         
18843         var r = false;
18844         
18845         for(var i in group){
18846             if(r){
18847                 break;
18848             }
18849             
18850             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18851         }
18852         
18853         return r;
18854     },
18855     
18856     /**
18857      * Mark this field as valid
18858      */
18859     markValid : function()
18860     {
18861         if(this.allowBlank){
18862             return;
18863         }
18864         
18865         var _this = this;
18866         
18867         this.fireEvent('valid', this);
18868         
18869         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18870         
18871         if(this.groupId){
18872             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18873         }
18874         
18875         if(label){
18876             label.markValid();
18877         }
18878         
18879         if(this.inputType == 'radio'){
18880             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18881                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18882                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18883             });
18884             
18885             return;
18886         }
18887         
18888         if(!this.groupId){
18889             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18890             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18891             return;
18892         }
18893         
18894         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18895             
18896         if(!group){
18897             return;
18898         }
18899         
18900         for(var i in group){
18901             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18902             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18903         }
18904     },
18905     
18906      /**
18907      * Mark this field as invalid
18908      * @param {String} msg The validation message
18909      */
18910     markInvalid : function(msg)
18911     {
18912         if(this.allowBlank){
18913             return;
18914         }
18915         
18916         var _this = this;
18917         
18918         this.fireEvent('invalid', this, msg);
18919         
18920         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18921         
18922         if(this.groupId){
18923             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18924         }
18925         
18926         if(label){
18927             label.markInvalid();
18928         }
18929             
18930         if(this.inputType == 'radio'){
18931             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18932                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18933                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18934             });
18935             
18936             return;
18937         }
18938         
18939         if(!this.groupId){
18940             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18941             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18942             return;
18943         }
18944         
18945         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18946         
18947         if(!group){
18948             return;
18949         }
18950         
18951         for(var i in group){
18952             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18953             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18954         }
18955         
18956     }
18957     
18958 });
18959
18960 Roo.apply(Roo.bootstrap.CheckBox, {
18961     
18962     groups: {},
18963     
18964      /**
18965     * register a CheckBox Group
18966     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18967     */
18968     register : function(checkbox)
18969     {
18970         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18971             this.groups[checkbox.groupId] = {};
18972         }
18973         
18974         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18975             return;
18976         }
18977         
18978         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18979         
18980     },
18981     /**
18982     * fetch a CheckBox Group based on the group ID
18983     * @param {string} the group ID
18984     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18985     */
18986     get: function(groupId) {
18987         if (typeof(this.groups[groupId]) == 'undefined') {
18988             return false;
18989         }
18990         
18991         return this.groups[groupId] ;
18992     }
18993     
18994     
18995 });
18996 /*
18997  * - LGPL
18998  *
18999  * Radio
19000  *
19001  *
19002  * not inline
19003  *<div class="radio">
19004   <label>
19005     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19006     Option one is this and that&mdash;be sure to include why it's great
19007   </label>
19008 </div>
19009  *
19010  *
19011  *inline
19012  *<span>
19013  *<label class="radio-inline">fieldLabel</label>
19014  *<label class="radio-inline">
19015   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19016 </label>
19017 <span>
19018  * 
19019  * 
19020  */
19021
19022 /**
19023  * @class Roo.bootstrap.Radio
19024  * @extends Roo.bootstrap.CheckBox
19025  * Bootstrap Radio class
19026
19027  * @constructor
19028  * Create a new Radio
19029  * @param {Object} config The config object
19030  */
19031
19032 Roo.bootstrap.Radio = function(config){
19033     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19034    
19035 };
19036
19037 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19038     
19039     inputType: 'radio',
19040     inputValue: '',
19041     valueOff: '',
19042     
19043     getAutoCreate : function()
19044     {
19045         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19046         align = align || 'left'; // default...
19047         
19048         
19049         
19050         var id = Roo.id();
19051         
19052         var cfg = {
19053                 tag : this.inline ? 'span' : 'div',
19054                 cls : '',
19055                 cn : []
19056         };
19057         
19058         var inline = this.inline ? ' radio-inline' : '';
19059         
19060         var lbl = {
19061                 tag: 'label' ,
19062                 // does not need for, as we wrap the input with it..
19063                 'for' : id,
19064                 cls : 'control-label box-label' + inline,
19065                 cn : []
19066         };
19067         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19068         
19069         var fieldLabel = {
19070             tag: 'label' ,
19071             //cls : 'control-label' + inline,
19072             html : this.fieldLabel,
19073             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19074         };
19075         
19076  
19077         
19078         
19079         var input =  {
19080             tag: 'input',
19081             id : id,
19082             type : this.inputType,
19083             //value : (!this.checked) ? this.valueOff : this.inputValue,
19084             value : this.inputValue,
19085             cls : 'roo-radio',
19086             placeholder : this.placeholder || '' // ?? needed????
19087             
19088         };
19089         if (this.weight) { // Validity check?
19090             input.cls += " radio-" + this.weight;
19091         }
19092         if (this.disabled) {
19093             input.disabled=true;
19094         }
19095         
19096         if(this.checked){
19097             input.checked = this.checked;
19098         }
19099         
19100         if (this.name) {
19101             input.name = this.name;
19102         }
19103         
19104         if (this.size) {
19105             input.cls += ' input-' + this.size;
19106         }
19107         
19108         //?? can span's inline have a width??
19109         
19110         var settings=this;
19111         ['xs','sm','md','lg'].map(function(size){
19112             if (settings[size]) {
19113                 cfg.cls += ' col-' + size + '-' + settings[size];
19114             }
19115         });
19116         
19117         var inputblock = input;
19118         
19119         if (this.before || this.after) {
19120             
19121             inputblock = {
19122                 cls : 'input-group',
19123                 tag : 'span',
19124                 cn :  [] 
19125             };
19126             if (this.before) {
19127                 inputblock.cn.push({
19128                     tag :'span',
19129                     cls : 'input-group-addon',
19130                     html : this.before
19131                 });
19132             }
19133             inputblock.cn.push(input);
19134             if (this.after) {
19135                 inputblock.cn.push({
19136                     tag :'span',
19137                     cls : 'input-group-addon',
19138                     html : this.after
19139                 });
19140             }
19141             
19142         };
19143         
19144         
19145         if (this.fieldLabel && this.fieldLabel.length) {
19146             cfg.cn.push(fieldLabel);
19147         }
19148        
19149         // normal bootstrap puts the input inside the label.
19150         // however with our styled version - it has to go after the input.
19151        
19152         //lbl.cn.push(inputblock);
19153         
19154         var lblwrap =  {
19155             tag: 'span',
19156             cls: 'radio' + inline,
19157             cn: [
19158                 inputblock,
19159                 lbl
19160             ]
19161         };
19162         
19163         cfg.cn.push( lblwrap);
19164         
19165         if(this.boxLabel){
19166             lbl.cn.push({
19167                 tag: 'span',
19168                 html: this.boxLabel
19169             })
19170         }
19171          
19172         
19173         return cfg;
19174         
19175     },
19176     
19177     initEvents : function()
19178     {
19179 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19180         
19181         this.inputEl().on('click', this.onClick,  this);
19182         if (this.boxLabel) {
19183             //Roo.log('find label');
19184             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19185         }
19186         
19187     },
19188     
19189     inputEl: function ()
19190     {
19191         return this.el.select('input.roo-radio',true).first();
19192     },
19193     onClick : function()
19194     {   
19195         Roo.log("click");
19196         this.setChecked(true);
19197     },
19198     
19199     setChecked : function(state,suppressEvent)
19200     {
19201         if(state){
19202             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19203                 v.dom.checked = false;
19204             });
19205         }
19206         Roo.log(this.inputEl().dom);
19207         this.checked = state;
19208         this.inputEl().dom.checked = state;
19209         
19210         if(suppressEvent !== true){
19211             this.fireEvent('check', this, state);
19212         }
19213         
19214         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19215         
19216     },
19217     
19218     getGroupValue : function()
19219     {
19220         var value = '';
19221         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19222             if(v.dom.checked == true){
19223                 value = v.dom.value;
19224             }
19225         });
19226         
19227         return value;
19228     },
19229     
19230     /**
19231      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19232      * @return {Mixed} value The field value
19233      */
19234     getValue : function(){
19235         return this.getGroupValue();
19236     }
19237     
19238 });
19239
19240  
19241 //<script type="text/javascript">
19242
19243 /*
19244  * Based  Ext JS Library 1.1.1
19245  * Copyright(c) 2006-2007, Ext JS, LLC.
19246  * LGPL
19247  *
19248  */
19249  
19250 /**
19251  * @class Roo.HtmlEditorCore
19252  * @extends Roo.Component
19253  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19254  *
19255  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19256  */
19257
19258 Roo.HtmlEditorCore = function(config){
19259     
19260     
19261     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19262     
19263     
19264     this.addEvents({
19265         /**
19266          * @event initialize
19267          * Fires when the editor is fully initialized (including the iframe)
19268          * @param {Roo.HtmlEditorCore} this
19269          */
19270         initialize: true,
19271         /**
19272          * @event activate
19273          * Fires when the editor is first receives the focus. Any insertion must wait
19274          * until after this event.
19275          * @param {Roo.HtmlEditorCore} this
19276          */
19277         activate: true,
19278          /**
19279          * @event beforesync
19280          * Fires before the textarea is updated with content from the editor iframe. Return false
19281          * to cancel the sync.
19282          * @param {Roo.HtmlEditorCore} this
19283          * @param {String} html
19284          */
19285         beforesync: true,
19286          /**
19287          * @event beforepush
19288          * Fires before the iframe editor is updated with content from the textarea. Return false
19289          * to cancel the push.
19290          * @param {Roo.HtmlEditorCore} this
19291          * @param {String} html
19292          */
19293         beforepush: true,
19294          /**
19295          * @event sync
19296          * Fires when the textarea is updated with content from the editor iframe.
19297          * @param {Roo.HtmlEditorCore} this
19298          * @param {String} html
19299          */
19300         sync: true,
19301          /**
19302          * @event push
19303          * Fires when the iframe editor is updated with content from the textarea.
19304          * @param {Roo.HtmlEditorCore} this
19305          * @param {String} html
19306          */
19307         push: true,
19308         
19309         /**
19310          * @event editorevent
19311          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19312          * @param {Roo.HtmlEditorCore} this
19313          */
19314         editorevent: true
19315         
19316     });
19317     
19318     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19319     
19320     // defaults : white / black...
19321     this.applyBlacklists();
19322     
19323     
19324     
19325 };
19326
19327
19328 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19329
19330
19331      /**
19332      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19333      */
19334     
19335     owner : false,
19336     
19337      /**
19338      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19339      *                        Roo.resizable.
19340      */
19341     resizable : false,
19342      /**
19343      * @cfg {Number} height (in pixels)
19344      */   
19345     height: 300,
19346    /**
19347      * @cfg {Number} width (in pixels)
19348      */   
19349     width: 500,
19350     
19351     /**
19352      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19353      * 
19354      */
19355     stylesheets: false,
19356     
19357     // id of frame..
19358     frameId: false,
19359     
19360     // private properties
19361     validationEvent : false,
19362     deferHeight: true,
19363     initialized : false,
19364     activated : false,
19365     sourceEditMode : false,
19366     onFocus : Roo.emptyFn,
19367     iframePad:3,
19368     hideMode:'offsets',
19369     
19370     clearUp: true,
19371     
19372     // blacklist + whitelisted elements..
19373     black: false,
19374     white: false,
19375      
19376     
19377
19378     /**
19379      * Protected method that will not generally be called directly. It
19380      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19381      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19382      */
19383     getDocMarkup : function(){
19384         // body styles..
19385         var st = '';
19386         
19387         // inherit styels from page...?? 
19388         if (this.stylesheets === false) {
19389             
19390             Roo.get(document.head).select('style').each(function(node) {
19391                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19392             });
19393             
19394             Roo.get(document.head).select('link').each(function(node) { 
19395                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19396             });
19397             
19398         } else if (!this.stylesheets.length) {
19399                 // simple..
19400                 st = '<style type="text/css">' +
19401                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19402                    '</style>';
19403         } else { 
19404             
19405         }
19406         
19407         st +=  '<style type="text/css">' +
19408             'IMG { cursor: pointer } ' +
19409         '</style>';
19410
19411         
19412         return '<html><head>' + st  +
19413             //<style type="text/css">' +
19414             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19415             //'</style>' +
19416             ' </head><body class="roo-htmleditor-body"></body></html>';
19417     },
19418
19419     // private
19420     onRender : function(ct, position)
19421     {
19422         var _t = this;
19423         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19424         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19425         
19426         
19427         this.el.dom.style.border = '0 none';
19428         this.el.dom.setAttribute('tabIndex', -1);
19429         this.el.addClass('x-hidden hide');
19430         
19431         
19432         
19433         if(Roo.isIE){ // fix IE 1px bogus margin
19434             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19435         }
19436        
19437         
19438         this.frameId = Roo.id();
19439         
19440          
19441         
19442         var iframe = this.owner.wrap.createChild({
19443             tag: 'iframe',
19444             cls: 'form-control', // bootstrap..
19445             id: this.frameId,
19446             name: this.frameId,
19447             frameBorder : 'no',
19448             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19449         }, this.el
19450         );
19451         
19452         
19453         this.iframe = iframe.dom;
19454
19455          this.assignDocWin();
19456         
19457         this.doc.designMode = 'on';
19458        
19459         this.doc.open();
19460         this.doc.write(this.getDocMarkup());
19461         this.doc.close();
19462
19463         
19464         var task = { // must defer to wait for browser to be ready
19465             run : function(){
19466                 //console.log("run task?" + this.doc.readyState);
19467                 this.assignDocWin();
19468                 if(this.doc.body || this.doc.readyState == 'complete'){
19469                     try {
19470                         this.doc.designMode="on";
19471                     } catch (e) {
19472                         return;
19473                     }
19474                     Roo.TaskMgr.stop(task);
19475                     this.initEditor.defer(10, this);
19476                 }
19477             },
19478             interval : 10,
19479             duration: 10000,
19480             scope: this
19481         };
19482         Roo.TaskMgr.start(task);
19483
19484     },
19485
19486     // private
19487     onResize : function(w, h)
19488     {
19489          Roo.log('resize: ' +w + ',' + h );
19490         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19491         if(!this.iframe){
19492             return;
19493         }
19494         if(typeof w == 'number'){
19495             
19496             this.iframe.style.width = w + 'px';
19497         }
19498         if(typeof h == 'number'){
19499             
19500             this.iframe.style.height = h + 'px';
19501             if(this.doc){
19502                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19503             }
19504         }
19505         
19506     },
19507
19508     /**
19509      * Toggles the editor between standard and source edit mode.
19510      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19511      */
19512     toggleSourceEdit : function(sourceEditMode){
19513         
19514         this.sourceEditMode = sourceEditMode === true;
19515         
19516         if(this.sourceEditMode){
19517  
19518             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19519             
19520         }else{
19521             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19522             //this.iframe.className = '';
19523             this.deferFocus();
19524         }
19525         //this.setSize(this.owner.wrap.getSize());
19526         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19527     },
19528
19529     
19530   
19531
19532     /**
19533      * Protected method that will not generally be called directly. If you need/want
19534      * custom HTML cleanup, this is the method you should override.
19535      * @param {String} html The HTML to be cleaned
19536      * return {String} The cleaned HTML
19537      */
19538     cleanHtml : function(html){
19539         html = String(html);
19540         if(html.length > 5){
19541             if(Roo.isSafari){ // strip safari nonsense
19542                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19543             }
19544         }
19545         if(html == '&nbsp;'){
19546             html = '';
19547         }
19548         return html;
19549     },
19550
19551     /**
19552      * HTML Editor -> Textarea
19553      * Protected method that will not generally be called directly. Syncs the contents
19554      * of the editor iframe with the textarea.
19555      */
19556     syncValue : function(){
19557         if(this.initialized){
19558             var bd = (this.doc.body || this.doc.documentElement);
19559             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19560             var html = bd.innerHTML;
19561             if(Roo.isSafari){
19562                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19563                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19564                 if(m && m[1]){
19565                     html = '<div style="'+m[0]+'">' + html + '</div>';
19566                 }
19567             }
19568             html = this.cleanHtml(html);
19569             // fix up the special chars.. normaly like back quotes in word...
19570             // however we do not want to do this with chinese..
19571             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19572                 var cc = b.charCodeAt();
19573                 if (
19574                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19575                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19576                     (cc >= 0xf900 && cc < 0xfb00 )
19577                 ) {
19578                         return b;
19579                 }
19580                 return "&#"+cc+";" 
19581             });
19582             if(this.owner.fireEvent('beforesync', this, html) !== false){
19583                 this.el.dom.value = html;
19584                 this.owner.fireEvent('sync', this, html);
19585             }
19586         }
19587     },
19588
19589     /**
19590      * Protected method that will not generally be called directly. Pushes the value of the textarea
19591      * into the iframe editor.
19592      */
19593     pushValue : function(){
19594         if(this.initialized){
19595             var v = this.el.dom.value.trim();
19596             
19597 //            if(v.length < 1){
19598 //                v = '&#160;';
19599 //            }
19600             
19601             if(this.owner.fireEvent('beforepush', this, v) !== false){
19602                 var d = (this.doc.body || this.doc.documentElement);
19603                 d.innerHTML = v;
19604                 this.cleanUpPaste();
19605                 this.el.dom.value = d.innerHTML;
19606                 this.owner.fireEvent('push', this, v);
19607             }
19608         }
19609     },
19610
19611     // private
19612     deferFocus : function(){
19613         this.focus.defer(10, this);
19614     },
19615
19616     // doc'ed in Field
19617     focus : function(){
19618         if(this.win && !this.sourceEditMode){
19619             this.win.focus();
19620         }else{
19621             this.el.focus();
19622         }
19623     },
19624     
19625     assignDocWin: function()
19626     {
19627         var iframe = this.iframe;
19628         
19629          if(Roo.isIE){
19630             this.doc = iframe.contentWindow.document;
19631             this.win = iframe.contentWindow;
19632         } else {
19633 //            if (!Roo.get(this.frameId)) {
19634 //                return;
19635 //            }
19636 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19637 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19638             
19639             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19640                 return;
19641             }
19642             
19643             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19644             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19645         }
19646     },
19647     
19648     // private
19649     initEditor : function(){
19650         //console.log("INIT EDITOR");
19651         this.assignDocWin();
19652         
19653         
19654         
19655         this.doc.designMode="on";
19656         this.doc.open();
19657         this.doc.write(this.getDocMarkup());
19658         this.doc.close();
19659         
19660         var dbody = (this.doc.body || this.doc.documentElement);
19661         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19662         // this copies styles from the containing element into thsi one..
19663         // not sure why we need all of this..
19664         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19665         
19666         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19667         //ss['background-attachment'] = 'fixed'; // w3c
19668         dbody.bgProperties = 'fixed'; // ie
19669         //Roo.DomHelper.applyStyles(dbody, ss);
19670         Roo.EventManager.on(this.doc, {
19671             //'mousedown': this.onEditorEvent,
19672             'mouseup': this.onEditorEvent,
19673             'dblclick': this.onEditorEvent,
19674             'click': this.onEditorEvent,
19675             'keyup': this.onEditorEvent,
19676             buffer:100,
19677             scope: this
19678         });
19679         if(Roo.isGecko){
19680             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19681         }
19682         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19683             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19684         }
19685         this.initialized = true;
19686
19687         this.owner.fireEvent('initialize', this);
19688         this.pushValue();
19689     },
19690
19691     // private
19692     onDestroy : function(){
19693         
19694         
19695         
19696         if(this.rendered){
19697             
19698             //for (var i =0; i < this.toolbars.length;i++) {
19699             //    // fixme - ask toolbars for heights?
19700             //    this.toolbars[i].onDestroy();
19701            // }
19702             
19703             //this.wrap.dom.innerHTML = '';
19704             //this.wrap.remove();
19705         }
19706     },
19707
19708     // private
19709     onFirstFocus : function(){
19710         
19711         this.assignDocWin();
19712         
19713         
19714         this.activated = true;
19715          
19716     
19717         if(Roo.isGecko){ // prevent silly gecko errors
19718             this.win.focus();
19719             var s = this.win.getSelection();
19720             if(!s.focusNode || s.focusNode.nodeType != 3){
19721                 var r = s.getRangeAt(0);
19722                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19723                 r.collapse(true);
19724                 this.deferFocus();
19725             }
19726             try{
19727                 this.execCmd('useCSS', true);
19728                 this.execCmd('styleWithCSS', false);
19729             }catch(e){}
19730         }
19731         this.owner.fireEvent('activate', this);
19732     },
19733
19734     // private
19735     adjustFont: function(btn){
19736         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19737         //if(Roo.isSafari){ // safari
19738         //    adjust *= 2;
19739        // }
19740         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19741         if(Roo.isSafari){ // safari
19742             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19743             v =  (v < 10) ? 10 : v;
19744             v =  (v > 48) ? 48 : v;
19745             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19746             
19747         }
19748         
19749         
19750         v = Math.max(1, v+adjust);
19751         
19752         this.execCmd('FontSize', v  );
19753     },
19754
19755     onEditorEvent : function(e)
19756     {
19757         this.owner.fireEvent('editorevent', this, e);
19758       //  this.updateToolbar();
19759         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19760     },
19761
19762     insertTag : function(tg)
19763     {
19764         // could be a bit smarter... -> wrap the current selected tRoo..
19765         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19766             
19767             range = this.createRange(this.getSelection());
19768             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19769             wrappingNode.appendChild(range.extractContents());
19770             range.insertNode(wrappingNode);
19771
19772             return;
19773             
19774             
19775             
19776         }
19777         this.execCmd("formatblock",   tg);
19778         
19779     },
19780     
19781     insertText : function(txt)
19782     {
19783         
19784         
19785         var range = this.createRange();
19786         range.deleteContents();
19787                //alert(Sender.getAttribute('label'));
19788                
19789         range.insertNode(this.doc.createTextNode(txt));
19790     } ,
19791     
19792      
19793
19794     /**
19795      * Executes a Midas editor command on the editor document and performs necessary focus and
19796      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19797      * @param {String} cmd The Midas command
19798      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19799      */
19800     relayCmd : function(cmd, value){
19801         this.win.focus();
19802         this.execCmd(cmd, value);
19803         this.owner.fireEvent('editorevent', this);
19804         //this.updateToolbar();
19805         this.owner.deferFocus();
19806     },
19807
19808     /**
19809      * Executes a Midas editor command directly on the editor document.
19810      * For visual commands, you should use {@link #relayCmd} instead.
19811      * <b>This should only be called after the editor is initialized.</b>
19812      * @param {String} cmd The Midas command
19813      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19814      */
19815     execCmd : function(cmd, value){
19816         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19817         this.syncValue();
19818     },
19819  
19820  
19821    
19822     /**
19823      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19824      * to insert tRoo.
19825      * @param {String} text | dom node.. 
19826      */
19827     insertAtCursor : function(text)
19828     {
19829         
19830         
19831         
19832         if(!this.activated){
19833             return;
19834         }
19835         /*
19836         if(Roo.isIE){
19837             this.win.focus();
19838             var r = this.doc.selection.createRange();
19839             if(r){
19840                 r.collapse(true);
19841                 r.pasteHTML(text);
19842                 this.syncValue();
19843                 this.deferFocus();
19844             
19845             }
19846             return;
19847         }
19848         */
19849         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19850             this.win.focus();
19851             
19852             
19853             // from jquery ui (MIT licenced)
19854             var range, node;
19855             var win = this.win;
19856             
19857             if (win.getSelection && win.getSelection().getRangeAt) {
19858                 range = win.getSelection().getRangeAt(0);
19859                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19860                 range.insertNode(node);
19861             } else if (win.document.selection && win.document.selection.createRange) {
19862                 // no firefox support
19863                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19864                 win.document.selection.createRange().pasteHTML(txt);
19865             } else {
19866                 // no firefox support
19867                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19868                 this.execCmd('InsertHTML', txt);
19869             } 
19870             
19871             this.syncValue();
19872             
19873             this.deferFocus();
19874         }
19875     },
19876  // private
19877     mozKeyPress : function(e){
19878         if(e.ctrlKey){
19879             var c = e.getCharCode(), cmd;
19880           
19881             if(c > 0){
19882                 c = String.fromCharCode(c).toLowerCase();
19883                 switch(c){
19884                     case 'b':
19885                         cmd = 'bold';
19886                         break;
19887                     case 'i':
19888                         cmd = 'italic';
19889                         break;
19890                     
19891                     case 'u':
19892                         cmd = 'underline';
19893                         break;
19894                     
19895                     case 'v':
19896                         this.cleanUpPaste.defer(100, this);
19897                         return;
19898                         
19899                 }
19900                 if(cmd){
19901                     this.win.focus();
19902                     this.execCmd(cmd);
19903                     this.deferFocus();
19904                     e.preventDefault();
19905                 }
19906                 
19907             }
19908         }
19909     },
19910
19911     // private
19912     fixKeys : function(){ // load time branching for fastest keydown performance
19913         if(Roo.isIE){
19914             return function(e){
19915                 var k = e.getKey(), r;
19916                 if(k == e.TAB){
19917                     e.stopEvent();
19918                     r = this.doc.selection.createRange();
19919                     if(r){
19920                         r.collapse(true);
19921                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19922                         this.deferFocus();
19923                     }
19924                     return;
19925                 }
19926                 
19927                 if(k == e.ENTER){
19928                     r = this.doc.selection.createRange();
19929                     if(r){
19930                         var target = r.parentElement();
19931                         if(!target || target.tagName.toLowerCase() != 'li'){
19932                             e.stopEvent();
19933                             r.pasteHTML('<br />');
19934                             r.collapse(false);
19935                             r.select();
19936                         }
19937                     }
19938                 }
19939                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19940                     this.cleanUpPaste.defer(100, this);
19941                     return;
19942                 }
19943                 
19944                 
19945             };
19946         }else if(Roo.isOpera){
19947             return function(e){
19948                 var k = e.getKey();
19949                 if(k == e.TAB){
19950                     e.stopEvent();
19951                     this.win.focus();
19952                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19953                     this.deferFocus();
19954                 }
19955                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19956                     this.cleanUpPaste.defer(100, this);
19957                     return;
19958                 }
19959                 
19960             };
19961         }else if(Roo.isSafari){
19962             return function(e){
19963                 var k = e.getKey();
19964                 
19965                 if(k == e.TAB){
19966                     e.stopEvent();
19967                     this.execCmd('InsertText','\t');
19968                     this.deferFocus();
19969                     return;
19970                 }
19971                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19972                     this.cleanUpPaste.defer(100, this);
19973                     return;
19974                 }
19975                 
19976              };
19977         }
19978     }(),
19979     
19980     getAllAncestors: function()
19981     {
19982         var p = this.getSelectedNode();
19983         var a = [];
19984         if (!p) {
19985             a.push(p); // push blank onto stack..
19986             p = this.getParentElement();
19987         }
19988         
19989         
19990         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19991             a.push(p);
19992             p = p.parentNode;
19993         }
19994         a.push(this.doc.body);
19995         return a;
19996     },
19997     lastSel : false,
19998     lastSelNode : false,
19999     
20000     
20001     getSelection : function() 
20002     {
20003         this.assignDocWin();
20004         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20005     },
20006     
20007     getSelectedNode: function() 
20008     {
20009         // this may only work on Gecko!!!
20010         
20011         // should we cache this!!!!
20012         
20013         
20014         
20015          
20016         var range = this.createRange(this.getSelection()).cloneRange();
20017         
20018         if (Roo.isIE) {
20019             var parent = range.parentElement();
20020             while (true) {
20021                 var testRange = range.duplicate();
20022                 testRange.moveToElementText(parent);
20023                 if (testRange.inRange(range)) {
20024                     break;
20025                 }
20026                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20027                     break;
20028                 }
20029                 parent = parent.parentElement;
20030             }
20031             return parent;
20032         }
20033         
20034         // is ancestor a text element.
20035         var ac =  range.commonAncestorContainer;
20036         if (ac.nodeType == 3) {
20037             ac = ac.parentNode;
20038         }
20039         
20040         var ar = ac.childNodes;
20041          
20042         var nodes = [];
20043         var other_nodes = [];
20044         var has_other_nodes = false;
20045         for (var i=0;i<ar.length;i++) {
20046             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20047                 continue;
20048             }
20049             // fullly contained node.
20050             
20051             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20052                 nodes.push(ar[i]);
20053                 continue;
20054             }
20055             
20056             // probably selected..
20057             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20058                 other_nodes.push(ar[i]);
20059                 continue;
20060             }
20061             // outer..
20062             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20063                 continue;
20064             }
20065             
20066             
20067             has_other_nodes = true;
20068         }
20069         if (!nodes.length && other_nodes.length) {
20070             nodes= other_nodes;
20071         }
20072         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20073             return false;
20074         }
20075         
20076         return nodes[0];
20077     },
20078     createRange: function(sel)
20079     {
20080         // this has strange effects when using with 
20081         // top toolbar - not sure if it's a great idea.
20082         //this.editor.contentWindow.focus();
20083         if (typeof sel != "undefined") {
20084             try {
20085                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20086             } catch(e) {
20087                 return this.doc.createRange();
20088             }
20089         } else {
20090             return this.doc.createRange();
20091         }
20092     },
20093     getParentElement: function()
20094     {
20095         
20096         this.assignDocWin();
20097         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20098         
20099         var range = this.createRange(sel);
20100          
20101         try {
20102             var p = range.commonAncestorContainer;
20103             while (p.nodeType == 3) { // text node
20104                 p = p.parentNode;
20105             }
20106             return p;
20107         } catch (e) {
20108             return null;
20109         }
20110     
20111     },
20112     /***
20113      *
20114      * Range intersection.. the hard stuff...
20115      *  '-1' = before
20116      *  '0' = hits..
20117      *  '1' = after.
20118      *         [ -- selected range --- ]
20119      *   [fail]                        [fail]
20120      *
20121      *    basically..
20122      *      if end is before start or  hits it. fail.
20123      *      if start is after end or hits it fail.
20124      *
20125      *   if either hits (but other is outside. - then it's not 
20126      *   
20127      *    
20128      **/
20129     
20130     
20131     // @see http://www.thismuchiknow.co.uk/?p=64.
20132     rangeIntersectsNode : function(range, node)
20133     {
20134         var nodeRange = node.ownerDocument.createRange();
20135         try {
20136             nodeRange.selectNode(node);
20137         } catch (e) {
20138             nodeRange.selectNodeContents(node);
20139         }
20140     
20141         var rangeStartRange = range.cloneRange();
20142         rangeStartRange.collapse(true);
20143     
20144         var rangeEndRange = range.cloneRange();
20145         rangeEndRange.collapse(false);
20146     
20147         var nodeStartRange = nodeRange.cloneRange();
20148         nodeStartRange.collapse(true);
20149     
20150         var nodeEndRange = nodeRange.cloneRange();
20151         nodeEndRange.collapse(false);
20152     
20153         return rangeStartRange.compareBoundaryPoints(
20154                  Range.START_TO_START, nodeEndRange) == -1 &&
20155                rangeEndRange.compareBoundaryPoints(
20156                  Range.START_TO_START, nodeStartRange) == 1;
20157         
20158          
20159     },
20160     rangeCompareNode : function(range, node)
20161     {
20162         var nodeRange = node.ownerDocument.createRange();
20163         try {
20164             nodeRange.selectNode(node);
20165         } catch (e) {
20166             nodeRange.selectNodeContents(node);
20167         }
20168         
20169         
20170         range.collapse(true);
20171     
20172         nodeRange.collapse(true);
20173      
20174         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20175         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20176          
20177         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20178         
20179         var nodeIsBefore   =  ss == 1;
20180         var nodeIsAfter    = ee == -1;
20181         
20182         if (nodeIsBefore && nodeIsAfter) {
20183             return 0; // outer
20184         }
20185         if (!nodeIsBefore && nodeIsAfter) {
20186             return 1; //right trailed.
20187         }
20188         
20189         if (nodeIsBefore && !nodeIsAfter) {
20190             return 2;  // left trailed.
20191         }
20192         // fully contined.
20193         return 3;
20194     },
20195
20196     // private? - in a new class?
20197     cleanUpPaste :  function()
20198     {
20199         // cleans up the whole document..
20200         Roo.log('cleanuppaste');
20201         
20202         this.cleanUpChildren(this.doc.body);
20203         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20204         if (clean != this.doc.body.innerHTML) {
20205             this.doc.body.innerHTML = clean;
20206         }
20207         
20208     },
20209     
20210     cleanWordChars : function(input) {// change the chars to hex code
20211         var he = Roo.HtmlEditorCore;
20212         
20213         var output = input;
20214         Roo.each(he.swapCodes, function(sw) { 
20215             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20216             
20217             output = output.replace(swapper, sw[1]);
20218         });
20219         
20220         return output;
20221     },
20222     
20223     
20224     cleanUpChildren : function (n)
20225     {
20226         if (!n.childNodes.length) {
20227             return;
20228         }
20229         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20230            this.cleanUpChild(n.childNodes[i]);
20231         }
20232     },
20233     
20234     
20235         
20236     
20237     cleanUpChild : function (node)
20238     {
20239         var ed = this;
20240         //console.log(node);
20241         if (node.nodeName == "#text") {
20242             // clean up silly Windows -- stuff?
20243             return; 
20244         }
20245         if (node.nodeName == "#comment") {
20246             node.parentNode.removeChild(node);
20247             // clean up silly Windows -- stuff?
20248             return; 
20249         }
20250         var lcname = node.tagName.toLowerCase();
20251         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20252         // whitelist of tags..
20253         
20254         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20255             // remove node.
20256             node.parentNode.removeChild(node);
20257             return;
20258             
20259         }
20260         
20261         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20262         
20263         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20264         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20265         
20266         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20267         //    remove_keep_children = true;
20268         //}
20269         
20270         if (remove_keep_children) {
20271             this.cleanUpChildren(node);
20272             // inserts everything just before this node...
20273             while (node.childNodes.length) {
20274                 var cn = node.childNodes[0];
20275                 node.removeChild(cn);
20276                 node.parentNode.insertBefore(cn, node);
20277             }
20278             node.parentNode.removeChild(node);
20279             return;
20280         }
20281         
20282         if (!node.attributes || !node.attributes.length) {
20283             this.cleanUpChildren(node);
20284             return;
20285         }
20286         
20287         function cleanAttr(n,v)
20288         {
20289             
20290             if (v.match(/^\./) || v.match(/^\//)) {
20291                 return;
20292             }
20293             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20294                 return;
20295             }
20296             if (v.match(/^#/)) {
20297                 return;
20298             }
20299 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20300             node.removeAttribute(n);
20301             
20302         }
20303         
20304         var cwhite = this.cwhite;
20305         var cblack = this.cblack;
20306             
20307         function cleanStyle(n,v)
20308         {
20309             if (v.match(/expression/)) { //XSS?? should we even bother..
20310                 node.removeAttribute(n);
20311                 return;
20312             }
20313             
20314             var parts = v.split(/;/);
20315             var clean = [];
20316             
20317             Roo.each(parts, function(p) {
20318                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20319                 if (!p.length) {
20320                     return true;
20321                 }
20322                 var l = p.split(':').shift().replace(/\s+/g,'');
20323                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20324                 
20325                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20326 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20327                     //node.removeAttribute(n);
20328                     return true;
20329                 }
20330                 //Roo.log()
20331                 // only allow 'c whitelisted system attributes'
20332                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20333 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20334                     //node.removeAttribute(n);
20335                     return true;
20336                 }
20337                 
20338                 
20339                  
20340                 
20341                 clean.push(p);
20342                 return true;
20343             });
20344             if (clean.length) { 
20345                 node.setAttribute(n, clean.join(';'));
20346             } else {
20347                 node.removeAttribute(n);
20348             }
20349             
20350         }
20351         
20352         
20353         for (var i = node.attributes.length-1; i > -1 ; i--) {
20354             var a = node.attributes[i];
20355             //console.log(a);
20356             
20357             if (a.name.toLowerCase().substr(0,2)=='on')  {
20358                 node.removeAttribute(a.name);
20359                 continue;
20360             }
20361             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20362                 node.removeAttribute(a.name);
20363                 continue;
20364             }
20365             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20366                 cleanAttr(a.name,a.value); // fixme..
20367                 continue;
20368             }
20369             if (a.name == 'style') {
20370                 cleanStyle(a.name,a.value);
20371                 continue;
20372             }
20373             /// clean up MS crap..
20374             // tecnically this should be a list of valid class'es..
20375             
20376             
20377             if (a.name == 'class') {
20378                 if (a.value.match(/^Mso/)) {
20379                     node.className = '';
20380                 }
20381                 
20382                 if (a.value.match(/body/)) {
20383                     node.className = '';
20384                 }
20385                 continue;
20386             }
20387             
20388             // style cleanup!?
20389             // class cleanup?
20390             
20391         }
20392         
20393         
20394         this.cleanUpChildren(node);
20395         
20396         
20397     },
20398     
20399     /**
20400      * Clean up MS wordisms...
20401      */
20402     cleanWord : function(node)
20403     {
20404         
20405         
20406         if (!node) {
20407             this.cleanWord(this.doc.body);
20408             return;
20409         }
20410         if (node.nodeName == "#text") {
20411             // clean up silly Windows -- stuff?
20412             return; 
20413         }
20414         if (node.nodeName == "#comment") {
20415             node.parentNode.removeChild(node);
20416             // clean up silly Windows -- stuff?
20417             return; 
20418         }
20419         
20420         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20421             node.parentNode.removeChild(node);
20422             return;
20423         }
20424         
20425         // remove - but keep children..
20426         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20427             while (node.childNodes.length) {
20428                 var cn = node.childNodes[0];
20429                 node.removeChild(cn);
20430                 node.parentNode.insertBefore(cn, node);
20431             }
20432             node.parentNode.removeChild(node);
20433             this.iterateChildren(node, this.cleanWord);
20434             return;
20435         }
20436         // clean styles
20437         if (node.className.length) {
20438             
20439             var cn = node.className.split(/\W+/);
20440             var cna = [];
20441             Roo.each(cn, function(cls) {
20442                 if (cls.match(/Mso[a-zA-Z]+/)) {
20443                     return;
20444                 }
20445                 cna.push(cls);
20446             });
20447             node.className = cna.length ? cna.join(' ') : '';
20448             if (!cna.length) {
20449                 node.removeAttribute("class");
20450             }
20451         }
20452         
20453         if (node.hasAttribute("lang")) {
20454             node.removeAttribute("lang");
20455         }
20456         
20457         if (node.hasAttribute("style")) {
20458             
20459             var styles = node.getAttribute("style").split(";");
20460             var nstyle = [];
20461             Roo.each(styles, function(s) {
20462                 if (!s.match(/:/)) {
20463                     return;
20464                 }
20465                 var kv = s.split(":");
20466                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20467                     return;
20468                 }
20469                 // what ever is left... we allow.
20470                 nstyle.push(s);
20471             });
20472             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20473             if (!nstyle.length) {
20474                 node.removeAttribute('style');
20475             }
20476         }
20477         this.iterateChildren(node, this.cleanWord);
20478         
20479         
20480         
20481     },
20482     /**
20483      * iterateChildren of a Node, calling fn each time, using this as the scole..
20484      * @param {DomNode} node node to iterate children of.
20485      * @param {Function} fn method of this class to call on each item.
20486      */
20487     iterateChildren : function(node, fn)
20488     {
20489         if (!node.childNodes.length) {
20490                 return;
20491         }
20492         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20493            fn.call(this, node.childNodes[i])
20494         }
20495     },
20496     
20497     
20498     /**
20499      * cleanTableWidths.
20500      *
20501      * Quite often pasting from word etc.. results in tables with column and widths.
20502      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20503      *
20504      */
20505     cleanTableWidths : function(node)
20506     {
20507          
20508          
20509         if (!node) {
20510             this.cleanTableWidths(this.doc.body);
20511             return;
20512         }
20513         
20514         // ignore list...
20515         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20516             return; 
20517         }
20518         Roo.log(node.tagName);
20519         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20520             this.iterateChildren(node, this.cleanTableWidths);
20521             return;
20522         }
20523         if (node.hasAttribute('width')) {
20524             node.removeAttribute('width');
20525         }
20526         
20527          
20528         if (node.hasAttribute("style")) {
20529             // pretty basic...
20530             
20531             var styles = node.getAttribute("style").split(";");
20532             var nstyle = [];
20533             Roo.each(styles, function(s) {
20534                 if (!s.match(/:/)) {
20535                     return;
20536                 }
20537                 var kv = s.split(":");
20538                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20539                     return;
20540                 }
20541                 // what ever is left... we allow.
20542                 nstyle.push(s);
20543             });
20544             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20545             if (!nstyle.length) {
20546                 node.removeAttribute('style');
20547             }
20548         }
20549         
20550         this.iterateChildren(node, this.cleanTableWidths);
20551         
20552         
20553     },
20554     
20555     
20556     
20557     
20558     domToHTML : function(currentElement, depth, nopadtext) {
20559         
20560         depth = depth || 0;
20561         nopadtext = nopadtext || false;
20562     
20563         if (!currentElement) {
20564             return this.domToHTML(this.doc.body);
20565         }
20566         
20567         //Roo.log(currentElement);
20568         var j;
20569         var allText = false;
20570         var nodeName = currentElement.nodeName;
20571         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20572         
20573         if  (nodeName == '#text') {
20574             
20575             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20576         }
20577         
20578         
20579         var ret = '';
20580         if (nodeName != 'BODY') {
20581              
20582             var i = 0;
20583             // Prints the node tagName, such as <A>, <IMG>, etc
20584             if (tagName) {
20585                 var attr = [];
20586                 for(i = 0; i < currentElement.attributes.length;i++) {
20587                     // quoting?
20588                     var aname = currentElement.attributes.item(i).name;
20589                     if (!currentElement.attributes.item(i).value.length) {
20590                         continue;
20591                     }
20592                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20593                 }
20594                 
20595                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20596             } 
20597             else {
20598                 
20599                 // eack
20600             }
20601         } else {
20602             tagName = false;
20603         }
20604         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20605             return ret;
20606         }
20607         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20608             nopadtext = true;
20609         }
20610         
20611         
20612         // Traverse the tree
20613         i = 0;
20614         var currentElementChild = currentElement.childNodes.item(i);
20615         var allText = true;
20616         var innerHTML  = '';
20617         lastnode = '';
20618         while (currentElementChild) {
20619             // Formatting code (indent the tree so it looks nice on the screen)
20620             var nopad = nopadtext;
20621             if (lastnode == 'SPAN') {
20622                 nopad  = true;
20623             }
20624             // text
20625             if  (currentElementChild.nodeName == '#text') {
20626                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20627                 toadd = nopadtext ? toadd : toadd.trim();
20628                 if (!nopad && toadd.length > 80) {
20629                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20630                 }
20631                 innerHTML  += toadd;
20632                 
20633                 i++;
20634                 currentElementChild = currentElement.childNodes.item(i);
20635                 lastNode = '';
20636                 continue;
20637             }
20638             allText = false;
20639             
20640             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20641                 
20642             // Recursively traverse the tree structure of the child node
20643             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20644             lastnode = currentElementChild.nodeName;
20645             i++;
20646             currentElementChild=currentElement.childNodes.item(i);
20647         }
20648         
20649         ret += innerHTML;
20650         
20651         if (!allText) {
20652                 // The remaining code is mostly for formatting the tree
20653             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20654         }
20655         
20656         
20657         if (tagName) {
20658             ret+= "</"+tagName+">";
20659         }
20660         return ret;
20661         
20662     },
20663         
20664     applyBlacklists : function()
20665     {
20666         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20667         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20668         
20669         this.white = [];
20670         this.black = [];
20671         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20672             if (b.indexOf(tag) > -1) {
20673                 return;
20674             }
20675             this.white.push(tag);
20676             
20677         }, this);
20678         
20679         Roo.each(w, function(tag) {
20680             if (b.indexOf(tag) > -1) {
20681                 return;
20682             }
20683             if (this.white.indexOf(tag) > -1) {
20684                 return;
20685             }
20686             this.white.push(tag);
20687             
20688         }, this);
20689         
20690         
20691         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20692             if (w.indexOf(tag) > -1) {
20693                 return;
20694             }
20695             this.black.push(tag);
20696             
20697         }, this);
20698         
20699         Roo.each(b, function(tag) {
20700             if (w.indexOf(tag) > -1) {
20701                 return;
20702             }
20703             if (this.black.indexOf(tag) > -1) {
20704                 return;
20705             }
20706             this.black.push(tag);
20707             
20708         }, this);
20709         
20710         
20711         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20712         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20713         
20714         this.cwhite = [];
20715         this.cblack = [];
20716         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20717             if (b.indexOf(tag) > -1) {
20718                 return;
20719             }
20720             this.cwhite.push(tag);
20721             
20722         }, this);
20723         
20724         Roo.each(w, function(tag) {
20725             if (b.indexOf(tag) > -1) {
20726                 return;
20727             }
20728             if (this.cwhite.indexOf(tag) > -1) {
20729                 return;
20730             }
20731             this.cwhite.push(tag);
20732             
20733         }, this);
20734         
20735         
20736         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20737             if (w.indexOf(tag) > -1) {
20738                 return;
20739             }
20740             this.cblack.push(tag);
20741             
20742         }, this);
20743         
20744         Roo.each(b, function(tag) {
20745             if (w.indexOf(tag) > -1) {
20746                 return;
20747             }
20748             if (this.cblack.indexOf(tag) > -1) {
20749                 return;
20750             }
20751             this.cblack.push(tag);
20752             
20753         }, this);
20754     },
20755     
20756     setStylesheets : function(stylesheets)
20757     {
20758         if(typeof(stylesheets) == 'string'){
20759             Roo.get(this.iframe.contentDocument.head).createChild({
20760                 tag : 'link',
20761                 rel : 'stylesheet',
20762                 type : 'text/css',
20763                 href : stylesheets
20764             });
20765             
20766             return;
20767         }
20768         var _this = this;
20769      
20770         Roo.each(stylesheets, function(s) {
20771             if(!s.length){
20772                 return;
20773             }
20774             
20775             Roo.get(_this.iframe.contentDocument.head).createChild({
20776                 tag : 'link',
20777                 rel : 'stylesheet',
20778                 type : 'text/css',
20779                 href : s
20780             });
20781         });
20782
20783         
20784     },
20785     
20786     removeStylesheets : function()
20787     {
20788         var _this = this;
20789         
20790         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20791             s.remove();
20792         });
20793     }
20794     
20795     // hide stuff that is not compatible
20796     /**
20797      * @event blur
20798      * @hide
20799      */
20800     /**
20801      * @event change
20802      * @hide
20803      */
20804     /**
20805      * @event focus
20806      * @hide
20807      */
20808     /**
20809      * @event specialkey
20810      * @hide
20811      */
20812     /**
20813      * @cfg {String} fieldClass @hide
20814      */
20815     /**
20816      * @cfg {String} focusClass @hide
20817      */
20818     /**
20819      * @cfg {String} autoCreate @hide
20820      */
20821     /**
20822      * @cfg {String} inputType @hide
20823      */
20824     /**
20825      * @cfg {String} invalidClass @hide
20826      */
20827     /**
20828      * @cfg {String} invalidText @hide
20829      */
20830     /**
20831      * @cfg {String} msgFx @hide
20832      */
20833     /**
20834      * @cfg {String} validateOnBlur @hide
20835      */
20836 });
20837
20838 Roo.HtmlEditorCore.white = [
20839         'area', 'br', 'img', 'input', 'hr', 'wbr',
20840         
20841        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20842        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20843        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20844        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20845        'table',   'ul',         'xmp', 
20846        
20847        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20848       'thead',   'tr', 
20849      
20850       'dir', 'menu', 'ol', 'ul', 'dl',
20851        
20852       'embed',  'object'
20853 ];
20854
20855
20856 Roo.HtmlEditorCore.black = [
20857     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20858         'applet', // 
20859         'base',   'basefont', 'bgsound', 'blink',  'body', 
20860         'frame',  'frameset', 'head',    'html',   'ilayer', 
20861         'iframe', 'layer',  'link',     'meta',    'object',   
20862         'script', 'style' ,'title',  'xml' // clean later..
20863 ];
20864 Roo.HtmlEditorCore.clean = [
20865     'script', 'style', 'title', 'xml'
20866 ];
20867 Roo.HtmlEditorCore.remove = [
20868     'font'
20869 ];
20870 // attributes..
20871
20872 Roo.HtmlEditorCore.ablack = [
20873     'on'
20874 ];
20875     
20876 Roo.HtmlEditorCore.aclean = [ 
20877     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20878 ];
20879
20880 // protocols..
20881 Roo.HtmlEditorCore.pwhite= [
20882         'http',  'https',  'mailto'
20883 ];
20884
20885 // white listed style attributes.
20886 Roo.HtmlEditorCore.cwhite= [
20887       //  'text-align', /// default is to allow most things..
20888       
20889          
20890 //        'font-size'//??
20891 ];
20892
20893 // black listed style attributes.
20894 Roo.HtmlEditorCore.cblack= [
20895       //  'font-size' -- this can be set by the project 
20896 ];
20897
20898
20899 Roo.HtmlEditorCore.swapCodes   =[ 
20900     [    8211, "--" ], 
20901     [    8212, "--" ], 
20902     [    8216,  "'" ],  
20903     [    8217, "'" ],  
20904     [    8220, '"' ],  
20905     [    8221, '"' ],  
20906     [    8226, "*" ],  
20907     [    8230, "..." ]
20908 ]; 
20909
20910     /*
20911  * - LGPL
20912  *
20913  * HtmlEditor
20914  * 
20915  */
20916
20917 /**
20918  * @class Roo.bootstrap.HtmlEditor
20919  * @extends Roo.bootstrap.TextArea
20920  * Bootstrap HtmlEditor class
20921
20922  * @constructor
20923  * Create a new HtmlEditor
20924  * @param {Object} config The config object
20925  */
20926
20927 Roo.bootstrap.HtmlEditor = function(config){
20928     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20929     if (!this.toolbars) {
20930         this.toolbars = [];
20931     }
20932     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20933     this.addEvents({
20934             /**
20935              * @event initialize
20936              * Fires when the editor is fully initialized (including the iframe)
20937              * @param {HtmlEditor} this
20938              */
20939             initialize: true,
20940             /**
20941              * @event activate
20942              * Fires when the editor is first receives the focus. Any insertion must wait
20943              * until after this event.
20944              * @param {HtmlEditor} this
20945              */
20946             activate: true,
20947              /**
20948              * @event beforesync
20949              * Fires before the textarea is updated with content from the editor iframe. Return false
20950              * to cancel the sync.
20951              * @param {HtmlEditor} this
20952              * @param {String} html
20953              */
20954             beforesync: true,
20955              /**
20956              * @event beforepush
20957              * Fires before the iframe editor is updated with content from the textarea. Return false
20958              * to cancel the push.
20959              * @param {HtmlEditor} this
20960              * @param {String} html
20961              */
20962             beforepush: true,
20963              /**
20964              * @event sync
20965              * Fires when the textarea is updated with content from the editor iframe.
20966              * @param {HtmlEditor} this
20967              * @param {String} html
20968              */
20969             sync: true,
20970              /**
20971              * @event push
20972              * Fires when the iframe editor is updated with content from the textarea.
20973              * @param {HtmlEditor} this
20974              * @param {String} html
20975              */
20976             push: true,
20977              /**
20978              * @event editmodechange
20979              * Fires when the editor switches edit modes
20980              * @param {HtmlEditor} this
20981              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20982              */
20983             editmodechange: true,
20984             /**
20985              * @event editorevent
20986              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20987              * @param {HtmlEditor} this
20988              */
20989             editorevent: true,
20990             /**
20991              * @event firstfocus
20992              * Fires when on first focus - needed by toolbars..
20993              * @param {HtmlEditor} this
20994              */
20995             firstfocus: true,
20996             /**
20997              * @event autosave
20998              * Auto save the htmlEditor value as a file into Events
20999              * @param {HtmlEditor} this
21000              */
21001             autosave: true,
21002             /**
21003              * @event savedpreview
21004              * preview the saved version of htmlEditor
21005              * @param {HtmlEditor} this
21006              */
21007             savedpreview: true
21008         });
21009 };
21010
21011
21012 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21013     
21014     
21015       /**
21016      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21017      */
21018     toolbars : false,
21019    
21020      /**
21021      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21022      *                        Roo.resizable.
21023      */
21024     resizable : false,
21025      /**
21026      * @cfg {Number} height (in pixels)
21027      */   
21028     height: 300,
21029    /**
21030      * @cfg {Number} width (in pixels)
21031      */   
21032     width: false,
21033     
21034     /**
21035      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21036      * 
21037      */
21038     stylesheets: false,
21039     
21040     // id of frame..
21041     frameId: false,
21042     
21043     // private properties
21044     validationEvent : false,
21045     deferHeight: true,
21046     initialized : false,
21047     activated : false,
21048     
21049     onFocus : Roo.emptyFn,
21050     iframePad:3,
21051     hideMode:'offsets',
21052     
21053     
21054     tbContainer : false,
21055     
21056     toolbarContainer :function() {
21057         return this.wrap.select('.x-html-editor-tb',true).first();
21058     },
21059
21060     /**
21061      * Protected method that will not generally be called directly. It
21062      * is called when the editor creates its toolbar. Override this method if you need to
21063      * add custom toolbar buttons.
21064      * @param {HtmlEditor} editor
21065      */
21066     createToolbar : function(){
21067         
21068         Roo.log("create toolbars");
21069         
21070         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21071         this.toolbars[0].render(this.toolbarContainer());
21072         
21073         return;
21074         
21075 //        if (!editor.toolbars || !editor.toolbars.length) {
21076 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21077 //        }
21078 //        
21079 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21080 //            editor.toolbars[i] = Roo.factory(
21081 //                    typeof(editor.toolbars[i]) == 'string' ?
21082 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21083 //                Roo.bootstrap.HtmlEditor);
21084 //            editor.toolbars[i].init(editor);
21085 //        }
21086     },
21087
21088      
21089     // private
21090     onRender : function(ct, position)
21091     {
21092        // Roo.log("Call onRender: " + this.xtype);
21093         var _t = this;
21094         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21095       
21096         this.wrap = this.inputEl().wrap({
21097             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21098         });
21099         
21100         this.editorcore.onRender(ct, position);
21101          
21102         if (this.resizable) {
21103             this.resizeEl = new Roo.Resizable(this.wrap, {
21104                 pinned : true,
21105                 wrap: true,
21106                 dynamic : true,
21107                 minHeight : this.height,
21108                 height: this.height,
21109                 handles : this.resizable,
21110                 width: this.width,
21111                 listeners : {
21112                     resize : function(r, w, h) {
21113                         _t.onResize(w,h); // -something
21114                     }
21115                 }
21116             });
21117             
21118         }
21119         this.createToolbar(this);
21120        
21121         
21122         if(!this.width && this.resizable){
21123             this.setSize(this.wrap.getSize());
21124         }
21125         if (this.resizeEl) {
21126             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21127             // should trigger onReize..
21128         }
21129         
21130     },
21131
21132     // private
21133     onResize : function(w, h)
21134     {
21135         Roo.log('resize: ' +w + ',' + h );
21136         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21137         var ew = false;
21138         var eh = false;
21139         
21140         if(this.inputEl() ){
21141             if(typeof w == 'number'){
21142                 var aw = w - this.wrap.getFrameWidth('lr');
21143                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21144                 ew = aw;
21145             }
21146             if(typeof h == 'number'){
21147                  var tbh = -11;  // fixme it needs to tool bar size!
21148                 for (var i =0; i < this.toolbars.length;i++) {
21149                     // fixme - ask toolbars for heights?
21150                     tbh += this.toolbars[i].el.getHeight();
21151                     //if (this.toolbars[i].footer) {
21152                     //    tbh += this.toolbars[i].footer.el.getHeight();
21153                     //}
21154                 }
21155               
21156                 
21157                 
21158                 
21159                 
21160                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21161                 ah -= 5; // knock a few pixes off for look..
21162                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21163                 var eh = ah;
21164             }
21165         }
21166         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21167         this.editorcore.onResize(ew,eh);
21168         
21169     },
21170
21171     /**
21172      * Toggles the editor between standard and source edit mode.
21173      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21174      */
21175     toggleSourceEdit : function(sourceEditMode)
21176     {
21177         this.editorcore.toggleSourceEdit(sourceEditMode);
21178         
21179         if(this.editorcore.sourceEditMode){
21180             Roo.log('editor - showing textarea');
21181             
21182 //            Roo.log('in');
21183 //            Roo.log(this.syncValue());
21184             this.syncValue();
21185             this.inputEl().removeClass(['hide', 'x-hidden']);
21186             this.inputEl().dom.removeAttribute('tabIndex');
21187             this.inputEl().focus();
21188         }else{
21189             Roo.log('editor - hiding textarea');
21190 //            Roo.log('out')
21191 //            Roo.log(this.pushValue()); 
21192             this.pushValue();
21193             
21194             this.inputEl().addClass(['hide', 'x-hidden']);
21195             this.inputEl().dom.setAttribute('tabIndex', -1);
21196             //this.deferFocus();
21197         }
21198          
21199         if(this.resizable){
21200             this.setSize(this.wrap.getSize());
21201         }
21202         
21203         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21204     },
21205  
21206     // private (for BoxComponent)
21207     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21208
21209     // private (for BoxComponent)
21210     getResizeEl : function(){
21211         return this.wrap;
21212     },
21213
21214     // private (for BoxComponent)
21215     getPositionEl : function(){
21216         return this.wrap;
21217     },
21218
21219     // private
21220     initEvents : function(){
21221         this.originalValue = this.getValue();
21222     },
21223
21224 //    /**
21225 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21226 //     * @method
21227 //     */
21228 //    markInvalid : Roo.emptyFn,
21229 //    /**
21230 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21231 //     * @method
21232 //     */
21233 //    clearInvalid : Roo.emptyFn,
21234
21235     setValue : function(v){
21236         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21237         this.editorcore.pushValue();
21238     },
21239
21240      
21241     // private
21242     deferFocus : function(){
21243         this.focus.defer(10, this);
21244     },
21245
21246     // doc'ed in Field
21247     focus : function(){
21248         this.editorcore.focus();
21249         
21250     },
21251       
21252
21253     // private
21254     onDestroy : function(){
21255         
21256         
21257         
21258         if(this.rendered){
21259             
21260             for (var i =0; i < this.toolbars.length;i++) {
21261                 // fixme - ask toolbars for heights?
21262                 this.toolbars[i].onDestroy();
21263             }
21264             
21265             this.wrap.dom.innerHTML = '';
21266             this.wrap.remove();
21267         }
21268     },
21269
21270     // private
21271     onFirstFocus : function(){
21272         //Roo.log("onFirstFocus");
21273         this.editorcore.onFirstFocus();
21274          for (var i =0; i < this.toolbars.length;i++) {
21275             this.toolbars[i].onFirstFocus();
21276         }
21277         
21278     },
21279     
21280     // private
21281     syncValue : function()
21282     {   
21283         this.editorcore.syncValue();
21284     },
21285     
21286     pushValue : function()
21287     {   
21288         this.editorcore.pushValue();
21289     }
21290      
21291     
21292     // hide stuff that is not compatible
21293     /**
21294      * @event blur
21295      * @hide
21296      */
21297     /**
21298      * @event change
21299      * @hide
21300      */
21301     /**
21302      * @event focus
21303      * @hide
21304      */
21305     /**
21306      * @event specialkey
21307      * @hide
21308      */
21309     /**
21310      * @cfg {String} fieldClass @hide
21311      */
21312     /**
21313      * @cfg {String} focusClass @hide
21314      */
21315     /**
21316      * @cfg {String} autoCreate @hide
21317      */
21318     /**
21319      * @cfg {String} inputType @hide
21320      */
21321     /**
21322      * @cfg {String} invalidClass @hide
21323      */
21324     /**
21325      * @cfg {String} invalidText @hide
21326      */
21327     /**
21328      * @cfg {String} msgFx @hide
21329      */
21330     /**
21331      * @cfg {String} validateOnBlur @hide
21332      */
21333 });
21334  
21335     
21336    
21337    
21338    
21339       
21340 Roo.namespace('Roo.bootstrap.htmleditor');
21341 /**
21342  * @class Roo.bootstrap.HtmlEditorToolbar1
21343  * Basic Toolbar
21344  * 
21345  * Usage:
21346  *
21347  new Roo.bootstrap.HtmlEditor({
21348     ....
21349     toolbars : [
21350         new Roo.bootstrap.HtmlEditorToolbar1({
21351             disable : { fonts: 1 , format: 1, ..., ... , ...],
21352             btns : [ .... ]
21353         })
21354     }
21355      
21356  * 
21357  * @cfg {Object} disable List of elements to disable..
21358  * @cfg {Array} btns List of additional buttons.
21359  * 
21360  * 
21361  * NEEDS Extra CSS? 
21362  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21363  */
21364  
21365 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21366 {
21367     
21368     Roo.apply(this, config);
21369     
21370     // default disabled, based on 'good practice'..
21371     this.disable = this.disable || {};
21372     Roo.applyIf(this.disable, {
21373         fontSize : true,
21374         colors : true,
21375         specialElements : true
21376     });
21377     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21378     
21379     this.editor = config.editor;
21380     this.editorcore = config.editor.editorcore;
21381     
21382     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21383     
21384     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21385     // dont call parent... till later.
21386 }
21387 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21388      
21389     bar : true,
21390     
21391     editor : false,
21392     editorcore : false,
21393     
21394     
21395     formats : [
21396         "p" ,  
21397         "h1","h2","h3","h4","h5","h6", 
21398         "pre", "code", 
21399         "abbr", "acronym", "address", "cite", "samp", "var",
21400         'div','span'
21401     ],
21402     
21403     onRender : function(ct, position)
21404     {
21405        // Roo.log("Call onRender: " + this.xtype);
21406         
21407        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21408        Roo.log(this.el);
21409        this.el.dom.style.marginBottom = '0';
21410        var _this = this;
21411        var editorcore = this.editorcore;
21412        var editor= this.editor;
21413        
21414        var children = [];
21415        var btn = function(id,cmd , toggle, handler){
21416        
21417             var  event = toggle ? 'toggle' : 'click';
21418        
21419             var a = {
21420                 size : 'sm',
21421                 xtype: 'Button',
21422                 xns: Roo.bootstrap,
21423                 glyphicon : id,
21424                 cmd : id || cmd,
21425                 enableToggle:toggle !== false,
21426                 //html : 'submit'
21427                 pressed : toggle ? false : null,
21428                 listeners : {}
21429             };
21430             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21431                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21432             };
21433             children.push(a);
21434             return a;
21435        }
21436         
21437         var style = {
21438                 xtype: 'Button',
21439                 size : 'sm',
21440                 xns: Roo.bootstrap,
21441                 glyphicon : 'font',
21442                 //html : 'submit'
21443                 menu : {
21444                     xtype: 'Menu',
21445                     xns: Roo.bootstrap,
21446                     items:  []
21447                 }
21448         };
21449         Roo.each(this.formats, function(f) {
21450             style.menu.items.push({
21451                 xtype :'MenuItem',
21452                 xns: Roo.bootstrap,
21453                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21454                 tagname : f,
21455                 listeners : {
21456                     click : function()
21457                     {
21458                         editorcore.insertTag(this.tagname);
21459                         editor.focus();
21460                     }
21461                 }
21462                 
21463             });
21464         });
21465          children.push(style);   
21466             
21467             
21468         btn('bold',false,true);
21469         btn('italic',false,true);
21470         btn('align-left', 'justifyleft',true);
21471         btn('align-center', 'justifycenter',true);
21472         btn('align-right' , 'justifyright',true);
21473         btn('link', false, false, function(btn) {
21474             //Roo.log("create link?");
21475             var url = prompt(this.createLinkText, this.defaultLinkValue);
21476             if(url && url != 'http:/'+'/'){
21477                 this.editorcore.relayCmd('createlink', url);
21478             }
21479         }),
21480         btn('list','insertunorderedlist',true);
21481         btn('pencil', false,true, function(btn){
21482                 Roo.log(this);
21483                 
21484                 this.toggleSourceEdit(btn.pressed);
21485         });
21486         /*
21487         var cog = {
21488                 xtype: 'Button',
21489                 size : 'sm',
21490                 xns: Roo.bootstrap,
21491                 glyphicon : 'cog',
21492                 //html : 'submit'
21493                 menu : {
21494                     xtype: 'Menu',
21495                     xns: Roo.bootstrap,
21496                     items:  []
21497                 }
21498         };
21499         
21500         cog.menu.items.push({
21501             xtype :'MenuItem',
21502             xns: Roo.bootstrap,
21503             html : Clean styles,
21504             tagname : f,
21505             listeners : {
21506                 click : function()
21507                 {
21508                     editorcore.insertTag(this.tagname);
21509                     editor.focus();
21510                 }
21511             }
21512             
21513         });
21514        */
21515         
21516          
21517        this.xtype = 'NavSimplebar';
21518         
21519         for(var i=0;i< children.length;i++) {
21520             
21521             this.buttons.add(this.addxtypeChild(children[i]));
21522             
21523         }
21524         
21525         editor.on('editorevent', this.updateToolbar, this);
21526     },
21527     onBtnClick : function(id)
21528     {
21529        this.editorcore.relayCmd(id);
21530        this.editorcore.focus();
21531     },
21532     
21533     /**
21534      * Protected method that will not generally be called directly. It triggers
21535      * a toolbar update by reading the markup state of the current selection in the editor.
21536      */
21537     updateToolbar: function(){
21538
21539         if(!this.editorcore.activated){
21540             this.editor.onFirstFocus(); // is this neeed?
21541             return;
21542         }
21543
21544         var btns = this.buttons; 
21545         var doc = this.editorcore.doc;
21546         btns.get('bold').setActive(doc.queryCommandState('bold'));
21547         btns.get('italic').setActive(doc.queryCommandState('italic'));
21548         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21549         
21550         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21551         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21552         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21553         
21554         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21555         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21556          /*
21557         
21558         var ans = this.editorcore.getAllAncestors();
21559         if (this.formatCombo) {
21560             
21561             
21562             var store = this.formatCombo.store;
21563             this.formatCombo.setValue("");
21564             for (var i =0; i < ans.length;i++) {
21565                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21566                     // select it..
21567                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21568                     break;
21569                 }
21570             }
21571         }
21572         
21573         
21574         
21575         // hides menus... - so this cant be on a menu...
21576         Roo.bootstrap.MenuMgr.hideAll();
21577         */
21578         Roo.bootstrap.MenuMgr.hideAll();
21579         //this.editorsyncValue();
21580     },
21581     onFirstFocus: function() {
21582         this.buttons.each(function(item){
21583            item.enable();
21584         });
21585     },
21586     toggleSourceEdit : function(sourceEditMode){
21587         
21588           
21589         if(sourceEditMode){
21590             Roo.log("disabling buttons");
21591            this.buttons.each( function(item){
21592                 if(item.cmd != 'pencil'){
21593                     item.disable();
21594                 }
21595             });
21596           
21597         }else{
21598             Roo.log("enabling buttons");
21599             if(this.editorcore.initialized){
21600                 this.buttons.each( function(item){
21601                     item.enable();
21602                 });
21603             }
21604             
21605         }
21606         Roo.log("calling toggole on editor");
21607         // tell the editor that it's been pressed..
21608         this.editor.toggleSourceEdit(sourceEditMode);
21609        
21610     }
21611 });
21612
21613
21614
21615
21616
21617 /**
21618  * @class Roo.bootstrap.Table.AbstractSelectionModel
21619  * @extends Roo.util.Observable
21620  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21621  * implemented by descendant classes.  This class should not be directly instantiated.
21622  * @constructor
21623  */
21624 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21625     this.locked = false;
21626     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21627 };
21628
21629
21630 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21631     /** @ignore Called by the grid automatically. Do not call directly. */
21632     init : function(grid){
21633         this.grid = grid;
21634         this.initEvents();
21635     },
21636
21637     /**
21638      * Locks the selections.
21639      */
21640     lock : function(){
21641         this.locked = true;
21642     },
21643
21644     /**
21645      * Unlocks the selections.
21646      */
21647     unlock : function(){
21648         this.locked = false;
21649     },
21650
21651     /**
21652      * Returns true if the selections are locked.
21653      * @return {Boolean}
21654      */
21655     isLocked : function(){
21656         return this.locked;
21657     }
21658 });
21659 /**
21660  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21661  * @class Roo.bootstrap.Table.RowSelectionModel
21662  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21663  * It supports multiple selections and keyboard selection/navigation. 
21664  * @constructor
21665  * @param {Object} config
21666  */
21667
21668 Roo.bootstrap.Table.RowSelectionModel = function(config){
21669     Roo.apply(this, config);
21670     this.selections = new Roo.util.MixedCollection(false, function(o){
21671         return o.id;
21672     });
21673
21674     this.last = false;
21675     this.lastActive = false;
21676
21677     this.addEvents({
21678         /**
21679              * @event selectionchange
21680              * Fires when the selection changes
21681              * @param {SelectionModel} this
21682              */
21683             "selectionchange" : true,
21684         /**
21685              * @event afterselectionchange
21686              * Fires after the selection changes (eg. by key press or clicking)
21687              * @param {SelectionModel} this
21688              */
21689             "afterselectionchange" : true,
21690         /**
21691              * @event beforerowselect
21692              * Fires when a row is selected being selected, return false to cancel.
21693              * @param {SelectionModel} this
21694              * @param {Number} rowIndex The selected index
21695              * @param {Boolean} keepExisting False if other selections will be cleared
21696              */
21697             "beforerowselect" : true,
21698         /**
21699              * @event rowselect
21700              * Fires when a row is selected.
21701              * @param {SelectionModel} this
21702              * @param {Number} rowIndex The selected index
21703              * @param {Roo.data.Record} r The record
21704              */
21705             "rowselect" : true,
21706         /**
21707              * @event rowdeselect
21708              * Fires when a row is deselected.
21709              * @param {SelectionModel} this
21710              * @param {Number} rowIndex The selected index
21711              */
21712         "rowdeselect" : true
21713     });
21714     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21715     this.locked = false;
21716 };
21717
21718 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21719     /**
21720      * @cfg {Boolean} singleSelect
21721      * True to allow selection of only one row at a time (defaults to false)
21722      */
21723     singleSelect : false,
21724
21725     // private
21726     initEvents : function(){
21727
21728         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21729             this.grid.on("mousedown", this.handleMouseDown, this);
21730         }else{ // allow click to work like normal
21731             this.grid.on("rowclick", this.handleDragableRowClick, this);
21732         }
21733
21734         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21735             "up" : function(e){
21736                 if(!e.shiftKey){
21737                     this.selectPrevious(e.shiftKey);
21738                 }else if(this.last !== false && this.lastActive !== false){
21739                     var last = this.last;
21740                     this.selectRange(this.last,  this.lastActive-1);
21741                     this.grid.getView().focusRow(this.lastActive);
21742                     if(last !== false){
21743                         this.last = last;
21744                     }
21745                 }else{
21746                     this.selectFirstRow();
21747                 }
21748                 this.fireEvent("afterselectionchange", this);
21749             },
21750             "down" : function(e){
21751                 if(!e.shiftKey){
21752                     this.selectNext(e.shiftKey);
21753                 }else if(this.last !== false && this.lastActive !== false){
21754                     var last = this.last;
21755                     this.selectRange(this.last,  this.lastActive+1);
21756                     this.grid.getView().focusRow(this.lastActive);
21757                     if(last !== false){
21758                         this.last = last;
21759                     }
21760                 }else{
21761                     this.selectFirstRow();
21762                 }
21763                 this.fireEvent("afterselectionchange", this);
21764             },
21765             scope: this
21766         });
21767
21768         var view = this.grid.view;
21769         view.on("refresh", this.onRefresh, this);
21770         view.on("rowupdated", this.onRowUpdated, this);
21771         view.on("rowremoved", this.onRemove, this);
21772     },
21773
21774     // private
21775     onRefresh : function(){
21776         var ds = this.grid.dataSource, i, v = this.grid.view;
21777         var s = this.selections;
21778         s.each(function(r){
21779             if((i = ds.indexOfId(r.id)) != -1){
21780                 v.onRowSelect(i);
21781             }else{
21782                 s.remove(r);
21783             }
21784         });
21785     },
21786
21787     // private
21788     onRemove : function(v, index, r){
21789         this.selections.remove(r);
21790     },
21791
21792     // private
21793     onRowUpdated : function(v, index, r){
21794         if(this.isSelected(r)){
21795             v.onRowSelect(index);
21796         }
21797     },
21798
21799     /**
21800      * Select records.
21801      * @param {Array} records The records to select
21802      * @param {Boolean} keepExisting (optional) True to keep existing selections
21803      */
21804     selectRecords : function(records, keepExisting){
21805         if(!keepExisting){
21806             this.clearSelections();
21807         }
21808         var ds = this.grid.dataSource;
21809         for(var i = 0, len = records.length; i < len; i++){
21810             this.selectRow(ds.indexOf(records[i]), true);
21811         }
21812     },
21813
21814     /**
21815      * Gets the number of selected rows.
21816      * @return {Number}
21817      */
21818     getCount : function(){
21819         return this.selections.length;
21820     },
21821
21822     /**
21823      * Selects the first row in the grid.
21824      */
21825     selectFirstRow : function(){
21826         this.selectRow(0);
21827     },
21828
21829     /**
21830      * Select the last row.
21831      * @param {Boolean} keepExisting (optional) True to keep existing selections
21832      */
21833     selectLastRow : function(keepExisting){
21834         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21835     },
21836
21837     /**
21838      * Selects the row immediately following the last selected row.
21839      * @param {Boolean} keepExisting (optional) True to keep existing selections
21840      */
21841     selectNext : function(keepExisting){
21842         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21843             this.selectRow(this.last+1, keepExisting);
21844             this.grid.getView().focusRow(this.last);
21845         }
21846     },
21847
21848     /**
21849      * Selects the row that precedes the last selected row.
21850      * @param {Boolean} keepExisting (optional) True to keep existing selections
21851      */
21852     selectPrevious : function(keepExisting){
21853         if(this.last){
21854             this.selectRow(this.last-1, keepExisting);
21855             this.grid.getView().focusRow(this.last);
21856         }
21857     },
21858
21859     /**
21860      * Returns the selected records
21861      * @return {Array} Array of selected records
21862      */
21863     getSelections : function(){
21864         return [].concat(this.selections.items);
21865     },
21866
21867     /**
21868      * Returns the first selected record.
21869      * @return {Record}
21870      */
21871     getSelected : function(){
21872         return this.selections.itemAt(0);
21873     },
21874
21875
21876     /**
21877      * Clears all selections.
21878      */
21879     clearSelections : function(fast){
21880         if(this.locked) {
21881             return;
21882         }
21883         if(fast !== true){
21884             var ds = this.grid.dataSource;
21885             var s = this.selections;
21886             s.each(function(r){
21887                 this.deselectRow(ds.indexOfId(r.id));
21888             }, this);
21889             s.clear();
21890         }else{
21891             this.selections.clear();
21892         }
21893         this.last = false;
21894     },
21895
21896
21897     /**
21898      * Selects all rows.
21899      */
21900     selectAll : function(){
21901         if(this.locked) {
21902             return;
21903         }
21904         this.selections.clear();
21905         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21906             this.selectRow(i, true);
21907         }
21908     },
21909
21910     /**
21911      * Returns True if there is a selection.
21912      * @return {Boolean}
21913      */
21914     hasSelection : function(){
21915         return this.selections.length > 0;
21916     },
21917
21918     /**
21919      * Returns True if the specified row is selected.
21920      * @param {Number/Record} record The record or index of the record to check
21921      * @return {Boolean}
21922      */
21923     isSelected : function(index){
21924         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21925         return (r && this.selections.key(r.id) ? true : false);
21926     },
21927
21928     /**
21929      * Returns True if the specified record id is selected.
21930      * @param {String} id The id of record to check
21931      * @return {Boolean}
21932      */
21933     isIdSelected : function(id){
21934         return (this.selections.key(id) ? true : false);
21935     },
21936
21937     // private
21938     handleMouseDown : function(e, t){
21939         var view = this.grid.getView(), rowIndex;
21940         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21941             return;
21942         };
21943         if(e.shiftKey && this.last !== false){
21944             var last = this.last;
21945             this.selectRange(last, rowIndex, e.ctrlKey);
21946             this.last = last; // reset the last
21947             view.focusRow(rowIndex);
21948         }else{
21949             var isSelected = this.isSelected(rowIndex);
21950             if(e.button !== 0 && isSelected){
21951                 view.focusRow(rowIndex);
21952             }else if(e.ctrlKey && isSelected){
21953                 this.deselectRow(rowIndex);
21954             }else if(!isSelected){
21955                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21956                 view.focusRow(rowIndex);
21957             }
21958         }
21959         this.fireEvent("afterselectionchange", this);
21960     },
21961     // private
21962     handleDragableRowClick :  function(grid, rowIndex, e) 
21963     {
21964         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21965             this.selectRow(rowIndex, false);
21966             grid.view.focusRow(rowIndex);
21967              this.fireEvent("afterselectionchange", this);
21968         }
21969     },
21970     
21971     /**
21972      * Selects multiple rows.
21973      * @param {Array} rows Array of the indexes of the row to select
21974      * @param {Boolean} keepExisting (optional) True to keep existing selections
21975      */
21976     selectRows : function(rows, keepExisting){
21977         if(!keepExisting){
21978             this.clearSelections();
21979         }
21980         for(var i = 0, len = rows.length; i < len; i++){
21981             this.selectRow(rows[i], true);
21982         }
21983     },
21984
21985     /**
21986      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21987      * @param {Number} startRow The index of the first row in the range
21988      * @param {Number} endRow The index of the last row in the range
21989      * @param {Boolean} keepExisting (optional) True to retain existing selections
21990      */
21991     selectRange : function(startRow, endRow, keepExisting){
21992         if(this.locked) {
21993             return;
21994         }
21995         if(!keepExisting){
21996             this.clearSelections();
21997         }
21998         if(startRow <= endRow){
21999             for(var i = startRow; i <= endRow; i++){
22000                 this.selectRow(i, true);
22001             }
22002         }else{
22003             for(var i = startRow; i >= endRow; i--){
22004                 this.selectRow(i, true);
22005             }
22006         }
22007     },
22008
22009     /**
22010      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22011      * @param {Number} startRow The index of the first row in the range
22012      * @param {Number} endRow The index of the last row in the range
22013      */
22014     deselectRange : function(startRow, endRow, preventViewNotify){
22015         if(this.locked) {
22016             return;
22017         }
22018         for(var i = startRow; i <= endRow; i++){
22019             this.deselectRow(i, preventViewNotify);
22020         }
22021     },
22022
22023     /**
22024      * Selects a row.
22025      * @param {Number} row The index of the row to select
22026      * @param {Boolean} keepExisting (optional) True to keep existing selections
22027      */
22028     selectRow : function(index, keepExisting, preventViewNotify){
22029         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22030             return;
22031         }
22032         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22033             if(!keepExisting || this.singleSelect){
22034                 this.clearSelections();
22035             }
22036             var r = this.grid.dataSource.getAt(index);
22037             this.selections.add(r);
22038             this.last = this.lastActive = index;
22039             if(!preventViewNotify){
22040                 this.grid.getView().onRowSelect(index);
22041             }
22042             this.fireEvent("rowselect", this, index, r);
22043             this.fireEvent("selectionchange", this);
22044         }
22045     },
22046
22047     /**
22048      * Deselects a row.
22049      * @param {Number} row The index of the row to deselect
22050      */
22051     deselectRow : function(index, preventViewNotify){
22052         if(this.locked) {
22053             return;
22054         }
22055         if(this.last == index){
22056             this.last = false;
22057         }
22058         if(this.lastActive == index){
22059             this.lastActive = false;
22060         }
22061         var r = this.grid.dataSource.getAt(index);
22062         this.selections.remove(r);
22063         if(!preventViewNotify){
22064             this.grid.getView().onRowDeselect(index);
22065         }
22066         this.fireEvent("rowdeselect", this, index);
22067         this.fireEvent("selectionchange", this);
22068     },
22069
22070     // private
22071     restoreLast : function(){
22072         if(this._last){
22073             this.last = this._last;
22074         }
22075     },
22076
22077     // private
22078     acceptsNav : function(row, col, cm){
22079         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22080     },
22081
22082     // private
22083     onEditorKey : function(field, e){
22084         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22085         if(k == e.TAB){
22086             e.stopEvent();
22087             ed.completeEdit();
22088             if(e.shiftKey){
22089                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22090             }else{
22091                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22092             }
22093         }else if(k == e.ENTER && !e.ctrlKey){
22094             e.stopEvent();
22095             ed.completeEdit();
22096             if(e.shiftKey){
22097                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22098             }else{
22099                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22100             }
22101         }else if(k == e.ESC){
22102             ed.cancelEdit();
22103         }
22104         if(newCell){
22105             g.startEditing(newCell[0], newCell[1]);
22106         }
22107     }
22108 });/*
22109  * Based on:
22110  * Ext JS Library 1.1.1
22111  * Copyright(c) 2006-2007, Ext JS, LLC.
22112  *
22113  * Originally Released Under LGPL - original licence link has changed is not relivant.
22114  *
22115  * Fork - LGPL
22116  * <script type="text/javascript">
22117  */
22118  
22119 /**
22120  * @class Roo.bootstrap.PagingToolbar
22121  * @extends Roo.bootstrap.NavSimplebar
22122  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22123  * @constructor
22124  * Create a new PagingToolbar
22125  * @param {Object} config The config object
22126  * @param {Roo.data.Store} store
22127  */
22128 Roo.bootstrap.PagingToolbar = function(config)
22129 {
22130     // old args format still supported... - xtype is prefered..
22131         // created from xtype...
22132     
22133     this.ds = config.dataSource;
22134     
22135     if (config.store && !this.ds) {
22136         this.store= Roo.factory(config.store, Roo.data);
22137         this.ds = this.store;
22138         this.ds.xmodule = this.xmodule || false;
22139     }
22140     
22141     this.toolbarItems = [];
22142     if (config.items) {
22143         this.toolbarItems = config.items;
22144     }
22145     
22146     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22147     
22148     this.cursor = 0;
22149     
22150     if (this.ds) { 
22151         this.bind(this.ds);
22152     }
22153     
22154     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22155     
22156 };
22157
22158 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22159     /**
22160      * @cfg {Roo.data.Store} dataSource
22161      * The underlying data store providing the paged data
22162      */
22163     /**
22164      * @cfg {String/HTMLElement/Element} container
22165      * container The id or element that will contain the toolbar
22166      */
22167     /**
22168      * @cfg {Boolean} displayInfo
22169      * True to display the displayMsg (defaults to false)
22170      */
22171     /**
22172      * @cfg {Number} pageSize
22173      * The number of records to display per page (defaults to 20)
22174      */
22175     pageSize: 20,
22176     /**
22177      * @cfg {String} displayMsg
22178      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22179      */
22180     displayMsg : 'Displaying {0} - {1} of {2}',
22181     /**
22182      * @cfg {String} emptyMsg
22183      * The message to display when no records are found (defaults to "No data to display")
22184      */
22185     emptyMsg : 'No data to display',
22186     /**
22187      * Customizable piece of the default paging text (defaults to "Page")
22188      * @type String
22189      */
22190     beforePageText : "Page",
22191     /**
22192      * Customizable piece of the default paging text (defaults to "of %0")
22193      * @type String
22194      */
22195     afterPageText : "of {0}",
22196     /**
22197      * Customizable piece of the default paging text (defaults to "First Page")
22198      * @type String
22199      */
22200     firstText : "First Page",
22201     /**
22202      * Customizable piece of the default paging text (defaults to "Previous Page")
22203      * @type String
22204      */
22205     prevText : "Previous Page",
22206     /**
22207      * Customizable piece of the default paging text (defaults to "Next Page")
22208      * @type String
22209      */
22210     nextText : "Next Page",
22211     /**
22212      * Customizable piece of the default paging text (defaults to "Last Page")
22213      * @type String
22214      */
22215     lastText : "Last Page",
22216     /**
22217      * Customizable piece of the default paging text (defaults to "Refresh")
22218      * @type String
22219      */
22220     refreshText : "Refresh",
22221
22222     buttons : false,
22223     // private
22224     onRender : function(ct, position) 
22225     {
22226         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22227         this.navgroup.parentId = this.id;
22228         this.navgroup.onRender(this.el, null);
22229         // add the buttons to the navgroup
22230         
22231         if(this.displayInfo){
22232             Roo.log(this.el.select('ul.navbar-nav',true).first());
22233             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22234             this.displayEl = this.el.select('.x-paging-info', true).first();
22235 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22236 //            this.displayEl = navel.el.select('span',true).first();
22237         }
22238         
22239         var _this = this;
22240         
22241         if(this.buttons){
22242             Roo.each(_this.buttons, function(e){ // this might need to use render????
22243                Roo.factory(e).onRender(_this.el, null);
22244             });
22245         }
22246             
22247         Roo.each(_this.toolbarItems, function(e) {
22248             _this.navgroup.addItem(e);
22249         });
22250         
22251         
22252         this.first = this.navgroup.addItem({
22253             tooltip: this.firstText,
22254             cls: "prev",
22255             icon : 'fa fa-backward',
22256             disabled: true,
22257             preventDefault: true,
22258             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22259         });
22260         
22261         this.prev =  this.navgroup.addItem({
22262             tooltip: this.prevText,
22263             cls: "prev",
22264             icon : 'fa fa-step-backward',
22265             disabled: true,
22266             preventDefault: true,
22267             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22268         });
22269     //this.addSeparator();
22270         
22271         
22272         var field = this.navgroup.addItem( {
22273             tagtype : 'span',
22274             cls : 'x-paging-position',
22275             
22276             html : this.beforePageText  +
22277                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22278                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22279          } ); //?? escaped?
22280         
22281         this.field = field.el.select('input', true).first();
22282         this.field.on("keydown", this.onPagingKeydown, this);
22283         this.field.on("focus", function(){this.dom.select();});
22284     
22285     
22286         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22287         //this.field.setHeight(18);
22288         //this.addSeparator();
22289         this.next = this.navgroup.addItem({
22290             tooltip: this.nextText,
22291             cls: "next",
22292             html : ' <i class="fa fa-step-forward">',
22293             disabled: true,
22294             preventDefault: true,
22295             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22296         });
22297         this.last = this.navgroup.addItem({
22298             tooltip: this.lastText,
22299             icon : 'fa fa-forward',
22300             cls: "next",
22301             disabled: true,
22302             preventDefault: true,
22303             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22304         });
22305     //this.addSeparator();
22306         this.loading = this.navgroup.addItem({
22307             tooltip: this.refreshText,
22308             icon: 'fa fa-refresh',
22309             preventDefault: true,
22310             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22311         });
22312         
22313     },
22314
22315     // private
22316     updateInfo : function(){
22317         if(this.displayEl){
22318             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22319             var msg = count == 0 ?
22320                 this.emptyMsg :
22321                 String.format(
22322                     this.displayMsg,
22323                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22324                 );
22325             this.displayEl.update(msg);
22326         }
22327     },
22328
22329     // private
22330     onLoad : function(ds, r, o){
22331        this.cursor = o.params ? o.params.start : 0;
22332        var d = this.getPageData(),
22333             ap = d.activePage,
22334             ps = d.pages;
22335         
22336        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22337        this.field.dom.value = ap;
22338        this.first.setDisabled(ap == 1);
22339        this.prev.setDisabled(ap == 1);
22340        this.next.setDisabled(ap == ps);
22341        this.last.setDisabled(ap == ps);
22342        this.loading.enable();
22343        this.updateInfo();
22344     },
22345
22346     // private
22347     getPageData : function(){
22348         var total = this.ds.getTotalCount();
22349         return {
22350             total : total,
22351             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22352             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22353         };
22354     },
22355
22356     // private
22357     onLoadError : function(){
22358         this.loading.enable();
22359     },
22360
22361     // private
22362     onPagingKeydown : function(e){
22363         var k = e.getKey();
22364         var d = this.getPageData();
22365         if(k == e.RETURN){
22366             var v = this.field.dom.value, pageNum;
22367             if(!v || isNaN(pageNum = parseInt(v, 10))){
22368                 this.field.dom.value = d.activePage;
22369                 return;
22370             }
22371             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22372             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22373             e.stopEvent();
22374         }
22375         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))
22376         {
22377           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22378           this.field.dom.value = pageNum;
22379           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22380           e.stopEvent();
22381         }
22382         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22383         {
22384           var v = this.field.dom.value, pageNum; 
22385           var increment = (e.shiftKey) ? 10 : 1;
22386           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22387                 increment *= -1;
22388           }
22389           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22390             this.field.dom.value = d.activePage;
22391             return;
22392           }
22393           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22394           {
22395             this.field.dom.value = parseInt(v, 10) + increment;
22396             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22397             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22398           }
22399           e.stopEvent();
22400         }
22401     },
22402
22403     // private
22404     beforeLoad : function(){
22405         if(this.loading){
22406             this.loading.disable();
22407         }
22408     },
22409
22410     // private
22411     onClick : function(which){
22412         
22413         var ds = this.ds;
22414         if (!ds) {
22415             return;
22416         }
22417         
22418         switch(which){
22419             case "first":
22420                 ds.load({params:{start: 0, limit: this.pageSize}});
22421             break;
22422             case "prev":
22423                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22424             break;
22425             case "next":
22426                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22427             break;
22428             case "last":
22429                 var total = ds.getTotalCount();
22430                 var extra = total % this.pageSize;
22431                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22432                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22433             break;
22434             case "refresh":
22435                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22436             break;
22437         }
22438     },
22439
22440     /**
22441      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22442      * @param {Roo.data.Store} store The data store to unbind
22443      */
22444     unbind : function(ds){
22445         ds.un("beforeload", this.beforeLoad, this);
22446         ds.un("load", this.onLoad, this);
22447         ds.un("loadexception", this.onLoadError, this);
22448         ds.un("remove", this.updateInfo, this);
22449         ds.un("add", this.updateInfo, this);
22450         this.ds = undefined;
22451     },
22452
22453     /**
22454      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22455      * @param {Roo.data.Store} store The data store to bind
22456      */
22457     bind : function(ds){
22458         ds.on("beforeload", this.beforeLoad, this);
22459         ds.on("load", this.onLoad, this);
22460         ds.on("loadexception", this.onLoadError, this);
22461         ds.on("remove", this.updateInfo, this);
22462         ds.on("add", this.updateInfo, this);
22463         this.ds = ds;
22464     }
22465 });/*
22466  * - LGPL
22467  *
22468  * element
22469  * 
22470  */
22471
22472 /**
22473  * @class Roo.bootstrap.MessageBar
22474  * @extends Roo.bootstrap.Component
22475  * Bootstrap MessageBar class
22476  * @cfg {String} html contents of the MessageBar
22477  * @cfg {String} weight (info | success | warning | danger) default info
22478  * @cfg {String} beforeClass insert the bar before the given class
22479  * @cfg {Boolean} closable (true | false) default false
22480  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22481  * 
22482  * @constructor
22483  * Create a new Element
22484  * @param {Object} config The config object
22485  */
22486
22487 Roo.bootstrap.MessageBar = function(config){
22488     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22489 };
22490
22491 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22492     
22493     html: '',
22494     weight: 'info',
22495     closable: false,
22496     fixed: false,
22497     beforeClass: 'bootstrap-sticky-wrap',
22498     
22499     getAutoCreate : function(){
22500         
22501         var cfg = {
22502             tag: 'div',
22503             cls: 'alert alert-dismissable alert-' + this.weight,
22504             cn: [
22505                 {
22506                     tag: 'span',
22507                     cls: 'message',
22508                     html: this.html || ''
22509                 }
22510             ]
22511         };
22512         
22513         if(this.fixed){
22514             cfg.cls += ' alert-messages-fixed';
22515         }
22516         
22517         if(this.closable){
22518             cfg.cn.push({
22519                 tag: 'button',
22520                 cls: 'close',
22521                 html: 'x'
22522             });
22523         }
22524         
22525         return cfg;
22526     },
22527     
22528     onRender : function(ct, position)
22529     {
22530         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22531         
22532         if(!this.el){
22533             var cfg = Roo.apply({},  this.getAutoCreate());
22534             cfg.id = Roo.id();
22535             
22536             if (this.cls) {
22537                 cfg.cls += ' ' + this.cls;
22538             }
22539             if (this.style) {
22540                 cfg.style = this.style;
22541             }
22542             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22543             
22544             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22545         }
22546         
22547         this.el.select('>button.close').on('click', this.hide, this);
22548         
22549     },
22550     
22551     show : function()
22552     {
22553         if (!this.rendered) {
22554             this.render();
22555         }
22556         
22557         this.el.show();
22558         
22559         this.fireEvent('show', this);
22560         
22561     },
22562     
22563     hide : function()
22564     {
22565         if (!this.rendered) {
22566             this.render();
22567         }
22568         
22569         this.el.hide();
22570         
22571         this.fireEvent('hide', this);
22572     },
22573     
22574     update : function()
22575     {
22576 //        var e = this.el.dom.firstChild;
22577 //        
22578 //        if(this.closable){
22579 //            e = e.nextSibling;
22580 //        }
22581 //        
22582 //        e.data = this.html || '';
22583
22584         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22585     }
22586    
22587 });
22588
22589  
22590
22591      /*
22592  * - LGPL
22593  *
22594  * Graph
22595  * 
22596  */
22597
22598
22599 /**
22600  * @class Roo.bootstrap.Graph
22601  * @extends Roo.bootstrap.Component
22602  * Bootstrap Graph class
22603 > Prameters
22604  -sm {number} sm 4
22605  -md {number} md 5
22606  @cfg {String} graphtype  bar | vbar | pie
22607  @cfg {number} g_x coodinator | centre x (pie)
22608  @cfg {number} g_y coodinator | centre y (pie)
22609  @cfg {number} g_r radius (pie)
22610  @cfg {number} g_height height of the chart (respected by all elements in the set)
22611  @cfg {number} g_width width of the chart (respected by all elements in the set)
22612  @cfg {Object} title The title of the chart
22613     
22614  -{Array}  values
22615  -opts (object) options for the chart 
22616      o {
22617      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22618      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22619      o vgutter (number)
22620      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.
22621      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22622      o to
22623      o stretch (boolean)
22624      o }
22625  -opts (object) options for the pie
22626      o{
22627      o cut
22628      o startAngle (number)
22629      o endAngle (number)
22630      } 
22631  *
22632  * @constructor
22633  * Create a new Input
22634  * @param {Object} config The config object
22635  */
22636
22637 Roo.bootstrap.Graph = function(config){
22638     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22639     
22640     this.addEvents({
22641         // img events
22642         /**
22643          * @event click
22644          * The img click event for the img.
22645          * @param {Roo.EventObject} e
22646          */
22647         "click" : true
22648     });
22649 };
22650
22651 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22652     
22653     sm: 4,
22654     md: 5,
22655     graphtype: 'bar',
22656     g_height: 250,
22657     g_width: 400,
22658     g_x: 50,
22659     g_y: 50,
22660     g_r: 30,
22661     opts:{
22662         //g_colors: this.colors,
22663         g_type: 'soft',
22664         g_gutter: '20%'
22665
22666     },
22667     title : false,
22668
22669     getAutoCreate : function(){
22670         
22671         var cfg = {
22672             tag: 'div',
22673             html : null
22674         };
22675         
22676         
22677         return  cfg;
22678     },
22679
22680     onRender : function(ct,position){
22681         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22682         this.raphael = Raphael(this.el.dom);
22683         
22684                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22685                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22686                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22687                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22688                 /*
22689                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22690                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22691                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22692                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22693                 
22694                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22695                 r.barchart(330, 10, 300, 220, data1);
22696                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22697                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22698                 */
22699                 
22700                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22701                 // r.barchart(30, 30, 560, 250,  xdata, {
22702                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22703                 //     axis : "0 0 1 1",
22704                 //     axisxlabels :  xdata
22705                 //     //yvalues : cols,
22706                    
22707                 // });
22708 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22709 //        
22710 //        this.load(null,xdata,{
22711 //                axis : "0 0 1 1",
22712 //                axisxlabels :  xdata
22713 //                });
22714
22715     },
22716
22717     load : function(graphtype,xdata,opts){
22718         this.raphael.clear();
22719         if(!graphtype) {
22720             graphtype = this.graphtype;
22721         }
22722         if(!opts){
22723             opts = this.opts;
22724         }
22725         var r = this.raphael,
22726             fin = function () {
22727                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22728             },
22729             fout = function () {
22730                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22731             },
22732             pfin = function() {
22733                 this.sector.stop();
22734                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22735
22736                 if (this.label) {
22737                     this.label[0].stop();
22738                     this.label[0].attr({ r: 7.5 });
22739                     this.label[1].attr({ "font-weight": 800 });
22740                 }
22741             },
22742             pfout = function() {
22743                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22744
22745                 if (this.label) {
22746                     this.label[0].animate({ r: 5 }, 500, "bounce");
22747                     this.label[1].attr({ "font-weight": 400 });
22748                 }
22749             };
22750
22751         switch(graphtype){
22752             case 'bar':
22753                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22754                 break;
22755             case 'hbar':
22756                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22757                 break;
22758             case 'pie':
22759 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22760 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22761 //            
22762                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22763                 
22764                 break;
22765
22766         }
22767         
22768         if(this.title){
22769             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22770         }
22771         
22772     },
22773     
22774     setTitle: function(o)
22775     {
22776         this.title = o;
22777     },
22778     
22779     initEvents: function() {
22780         
22781         if(!this.href){
22782             this.el.on('click', this.onClick, this);
22783         }
22784     },
22785     
22786     onClick : function(e)
22787     {
22788         Roo.log('img onclick');
22789         this.fireEvent('click', this, e);
22790     }
22791    
22792 });
22793
22794  
22795 /*
22796  * - LGPL
22797  *
22798  * numberBox
22799  * 
22800  */
22801 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22802
22803 /**
22804  * @class Roo.bootstrap.dash.NumberBox
22805  * @extends Roo.bootstrap.Component
22806  * Bootstrap NumberBox class
22807  * @cfg {String} headline Box headline
22808  * @cfg {String} content Box content
22809  * @cfg {String} icon Box icon
22810  * @cfg {String} footer Footer text
22811  * @cfg {String} fhref Footer href
22812  * 
22813  * @constructor
22814  * Create a new NumberBox
22815  * @param {Object} config The config object
22816  */
22817
22818
22819 Roo.bootstrap.dash.NumberBox = function(config){
22820     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22821     
22822 };
22823
22824 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22825     
22826     headline : '',
22827     content : '',
22828     icon : '',
22829     footer : '',
22830     fhref : '',
22831     ficon : '',
22832     
22833     getAutoCreate : function(){
22834         
22835         var cfg = {
22836             tag : 'div',
22837             cls : 'small-box ',
22838             cn : [
22839                 {
22840                     tag : 'div',
22841                     cls : 'inner',
22842                     cn :[
22843                         {
22844                             tag : 'h3',
22845                             cls : 'roo-headline',
22846                             html : this.headline
22847                         },
22848                         {
22849                             tag : 'p',
22850                             cls : 'roo-content',
22851                             html : this.content
22852                         }
22853                     ]
22854                 }
22855             ]
22856         };
22857         
22858         if(this.icon){
22859             cfg.cn.push({
22860                 tag : 'div',
22861                 cls : 'icon',
22862                 cn :[
22863                     {
22864                         tag : 'i',
22865                         cls : 'ion ' + this.icon
22866                     }
22867                 ]
22868             });
22869         }
22870         
22871         if(this.footer){
22872             var footer = {
22873                 tag : 'a',
22874                 cls : 'small-box-footer',
22875                 href : this.fhref || '#',
22876                 html : this.footer
22877             };
22878             
22879             cfg.cn.push(footer);
22880             
22881         }
22882         
22883         return  cfg;
22884     },
22885
22886     onRender : function(ct,position){
22887         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22888
22889
22890        
22891                 
22892     },
22893
22894     setHeadline: function (value)
22895     {
22896         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22897     },
22898     
22899     setFooter: function (value, href)
22900     {
22901         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22902         
22903         if(href){
22904             this.el.select('a.small-box-footer',true).first().attr('href', href);
22905         }
22906         
22907     },
22908
22909     setContent: function (value)
22910     {
22911         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22912     },
22913
22914     initEvents: function() 
22915     {   
22916         
22917     }
22918     
22919 });
22920
22921  
22922 /*
22923  * - LGPL
22924  *
22925  * TabBox
22926  * 
22927  */
22928 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22929
22930 /**
22931  * @class Roo.bootstrap.dash.TabBox
22932  * @extends Roo.bootstrap.Component
22933  * Bootstrap TabBox class
22934  * @cfg {String} title Title of the TabBox
22935  * @cfg {String} icon Icon of the TabBox
22936  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22937  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22938  * 
22939  * @constructor
22940  * Create a new TabBox
22941  * @param {Object} config The config object
22942  */
22943
22944
22945 Roo.bootstrap.dash.TabBox = function(config){
22946     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22947     this.addEvents({
22948         // raw events
22949         /**
22950          * @event addpane
22951          * When a pane is added
22952          * @param {Roo.bootstrap.dash.TabPane} pane
22953          */
22954         "addpane" : true,
22955         /**
22956          * @event activatepane
22957          * When a pane is activated
22958          * @param {Roo.bootstrap.dash.TabPane} pane
22959          */
22960         "activatepane" : true
22961         
22962          
22963     });
22964     
22965     this.panes = [];
22966 };
22967
22968 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22969
22970     title : '',
22971     icon : false,
22972     showtabs : true,
22973     tabScrollable : false,
22974     
22975     getChildContainer : function()
22976     {
22977         return this.el.select('.tab-content', true).first();
22978     },
22979     
22980     getAutoCreate : function(){
22981         
22982         var header = {
22983             tag: 'li',
22984             cls: 'pull-left header',
22985             html: this.title,
22986             cn : []
22987         };
22988         
22989         if(this.icon){
22990             header.cn.push({
22991                 tag: 'i',
22992                 cls: 'fa ' + this.icon
22993             });
22994         }
22995         
22996         var h = {
22997             tag: 'ul',
22998             cls: 'nav nav-tabs pull-right',
22999             cn: [
23000                 header
23001             ]
23002         };
23003         
23004         if(this.tabScrollable){
23005             h = {
23006                 tag: 'div',
23007                 cls: 'tab-header',
23008                 cn: [
23009                     {
23010                         tag: 'ul',
23011                         cls: 'nav nav-tabs pull-right',
23012                         cn: [
23013                             header
23014                         ]
23015                     }
23016                 ]
23017             };
23018         }
23019         
23020         var cfg = {
23021             tag: 'div',
23022             cls: 'nav-tabs-custom',
23023             cn: [
23024                 h,
23025                 {
23026                     tag: 'div',
23027                     cls: 'tab-content no-padding',
23028                     cn: []
23029                 }
23030             ]
23031         };
23032
23033         return  cfg;
23034     },
23035     initEvents : function()
23036     {
23037         //Roo.log('add add pane handler');
23038         this.on('addpane', this.onAddPane, this);
23039     },
23040      /**
23041      * Updates the box title
23042      * @param {String} html to set the title to.
23043      */
23044     setTitle : function(value)
23045     {
23046         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23047     },
23048     onAddPane : function(pane)
23049     {
23050         this.panes.push(pane);
23051         //Roo.log('addpane');
23052         //Roo.log(pane);
23053         // tabs are rendere left to right..
23054         if(!this.showtabs){
23055             return;
23056         }
23057         
23058         var ctr = this.el.select('.nav-tabs', true).first();
23059          
23060          
23061         var existing = ctr.select('.nav-tab',true);
23062         var qty = existing.getCount();;
23063         
23064         
23065         var tab = ctr.createChild({
23066             tag : 'li',
23067             cls : 'nav-tab' + (qty ? '' : ' active'),
23068             cn : [
23069                 {
23070                     tag : 'a',
23071                     href:'#',
23072                     html : pane.title
23073                 }
23074             ]
23075         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23076         pane.tab = tab;
23077         
23078         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23079         if (!qty) {
23080             pane.el.addClass('active');
23081         }
23082         
23083                 
23084     },
23085     onTabClick : function(ev,un,ob,pane)
23086     {
23087         //Roo.log('tab - prev default');
23088         ev.preventDefault();
23089         
23090         
23091         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23092         pane.tab.addClass('active');
23093         //Roo.log(pane.title);
23094         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23095         // technically we should have a deactivate event.. but maybe add later.
23096         // and it should not de-activate the selected tab...
23097         this.fireEvent('activatepane', pane);
23098         pane.el.addClass('active');
23099         pane.fireEvent('activate');
23100         
23101         
23102     },
23103     
23104     getActivePane : function()
23105     {
23106         var r = false;
23107         Roo.each(this.panes, function(p) {
23108             if(p.el.hasClass('active')){
23109                 r = p;
23110                 return false;
23111             }
23112             
23113             return;
23114         });
23115         
23116         return r;
23117     }
23118     
23119     
23120 });
23121
23122  
23123 /*
23124  * - LGPL
23125  *
23126  * Tab pane
23127  * 
23128  */
23129 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23130 /**
23131  * @class Roo.bootstrap.TabPane
23132  * @extends Roo.bootstrap.Component
23133  * Bootstrap TabPane class
23134  * @cfg {Boolean} active (false | true) Default false
23135  * @cfg {String} title title of panel
23136
23137  * 
23138  * @constructor
23139  * Create a new TabPane
23140  * @param {Object} config The config object
23141  */
23142
23143 Roo.bootstrap.dash.TabPane = function(config){
23144     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23145     
23146     this.addEvents({
23147         // raw events
23148         /**
23149          * @event activate
23150          * When a pane is activated
23151          * @param {Roo.bootstrap.dash.TabPane} pane
23152          */
23153         "activate" : true
23154          
23155     });
23156 };
23157
23158 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23159     
23160     active : false,
23161     title : '',
23162     
23163     // the tabBox that this is attached to.
23164     tab : false,
23165      
23166     getAutoCreate : function() 
23167     {
23168         var cfg = {
23169             tag: 'div',
23170             cls: 'tab-pane'
23171         };
23172         
23173         if(this.active){
23174             cfg.cls += ' active';
23175         }
23176         
23177         return cfg;
23178     },
23179     initEvents  : function()
23180     {
23181         //Roo.log('trigger add pane handler');
23182         this.parent().fireEvent('addpane', this)
23183     },
23184     
23185      /**
23186      * Updates the tab title 
23187      * @param {String} html to set the title to.
23188      */
23189     setTitle: function(str)
23190     {
23191         if (!this.tab) {
23192             return;
23193         }
23194         this.title = str;
23195         this.tab.select('a', true).first().dom.innerHTML = str;
23196         
23197     }
23198     
23199     
23200     
23201 });
23202
23203  
23204
23205
23206  /*
23207  * - LGPL
23208  *
23209  * menu
23210  * 
23211  */
23212 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23213
23214 /**
23215  * @class Roo.bootstrap.menu.Menu
23216  * @extends Roo.bootstrap.Component
23217  * Bootstrap Menu class - container for Menu
23218  * @cfg {String} html Text of the menu
23219  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23220  * @cfg {String} icon Font awesome icon
23221  * @cfg {String} pos Menu align to (top | bottom) default bottom
23222  * 
23223  * 
23224  * @constructor
23225  * Create a new Menu
23226  * @param {Object} config The config object
23227  */
23228
23229
23230 Roo.bootstrap.menu.Menu = function(config){
23231     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23232     
23233     this.addEvents({
23234         /**
23235          * @event beforeshow
23236          * Fires before this menu is displayed
23237          * @param {Roo.bootstrap.menu.Menu} this
23238          */
23239         beforeshow : true,
23240         /**
23241          * @event beforehide
23242          * Fires before this menu is hidden
23243          * @param {Roo.bootstrap.menu.Menu} this
23244          */
23245         beforehide : true,
23246         /**
23247          * @event show
23248          * Fires after this menu is displayed
23249          * @param {Roo.bootstrap.menu.Menu} this
23250          */
23251         show : true,
23252         /**
23253          * @event hide
23254          * Fires after this menu is hidden
23255          * @param {Roo.bootstrap.menu.Menu} this
23256          */
23257         hide : true,
23258         /**
23259          * @event click
23260          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23261          * @param {Roo.bootstrap.menu.Menu} this
23262          * @param {Roo.EventObject} e
23263          */
23264         click : true
23265     });
23266     
23267 };
23268
23269 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23270     
23271     submenu : false,
23272     html : '',
23273     weight : 'default',
23274     icon : false,
23275     pos : 'bottom',
23276     
23277     
23278     getChildContainer : function() {
23279         if(this.isSubMenu){
23280             return this.el;
23281         }
23282         
23283         return this.el.select('ul.dropdown-menu', true).first();  
23284     },
23285     
23286     getAutoCreate : function()
23287     {
23288         var text = [
23289             {
23290                 tag : 'span',
23291                 cls : 'roo-menu-text',
23292                 html : this.html
23293             }
23294         ];
23295         
23296         if(this.icon){
23297             text.unshift({
23298                 tag : 'i',
23299                 cls : 'fa ' + this.icon
23300             })
23301         }
23302         
23303         
23304         var cfg = {
23305             tag : 'div',
23306             cls : 'btn-group',
23307             cn : [
23308                 {
23309                     tag : 'button',
23310                     cls : 'dropdown-button btn btn-' + this.weight,
23311                     cn : text
23312                 },
23313                 {
23314                     tag : 'button',
23315                     cls : 'dropdown-toggle btn btn-' + this.weight,
23316                     cn : [
23317                         {
23318                             tag : 'span',
23319                             cls : 'caret'
23320                         }
23321                     ]
23322                 },
23323                 {
23324                     tag : 'ul',
23325                     cls : 'dropdown-menu'
23326                 }
23327             ]
23328             
23329         };
23330         
23331         if(this.pos == 'top'){
23332             cfg.cls += ' dropup';
23333         }
23334         
23335         if(this.isSubMenu){
23336             cfg = {
23337                 tag : 'ul',
23338                 cls : 'dropdown-menu'
23339             }
23340         }
23341         
23342         return cfg;
23343     },
23344     
23345     onRender : function(ct, position)
23346     {
23347         this.isSubMenu = ct.hasClass('dropdown-submenu');
23348         
23349         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23350     },
23351     
23352     initEvents : function() 
23353     {
23354         if(this.isSubMenu){
23355             return;
23356         }
23357         
23358         this.hidden = true;
23359         
23360         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23361         this.triggerEl.on('click', this.onTriggerPress, this);
23362         
23363         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23364         this.buttonEl.on('click', this.onClick, this);
23365         
23366     },
23367     
23368     list : function()
23369     {
23370         if(this.isSubMenu){
23371             return this.el;
23372         }
23373         
23374         return this.el.select('ul.dropdown-menu', true).first();
23375     },
23376     
23377     onClick : function(e)
23378     {
23379         this.fireEvent("click", this, e);
23380     },
23381     
23382     onTriggerPress  : function(e)
23383     {   
23384         if (this.isVisible()) {
23385             this.hide();
23386         } else {
23387             this.show();
23388         }
23389     },
23390     
23391     isVisible : function(){
23392         return !this.hidden;
23393     },
23394     
23395     show : function()
23396     {
23397         this.fireEvent("beforeshow", this);
23398         
23399         this.hidden = false;
23400         this.el.addClass('open');
23401         
23402         Roo.get(document).on("mouseup", this.onMouseUp, this);
23403         
23404         this.fireEvent("show", this);
23405         
23406         
23407     },
23408     
23409     hide : function()
23410     {
23411         this.fireEvent("beforehide", this);
23412         
23413         this.hidden = true;
23414         this.el.removeClass('open');
23415         
23416         Roo.get(document).un("mouseup", this.onMouseUp);
23417         
23418         this.fireEvent("hide", this);
23419     },
23420     
23421     onMouseUp : function()
23422     {
23423         this.hide();
23424     }
23425     
23426 });
23427
23428  
23429  /*
23430  * - LGPL
23431  *
23432  * menu item
23433  * 
23434  */
23435 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23436
23437 /**
23438  * @class Roo.bootstrap.menu.Item
23439  * @extends Roo.bootstrap.Component
23440  * Bootstrap MenuItem class
23441  * @cfg {Boolean} submenu (true | false) default false
23442  * @cfg {String} html text of the item
23443  * @cfg {String} href the link
23444  * @cfg {Boolean} disable (true | false) default false
23445  * @cfg {Boolean} preventDefault (true | false) default true
23446  * @cfg {String} icon Font awesome icon
23447  * @cfg {String} pos Submenu align to (left | right) default right 
23448  * 
23449  * 
23450  * @constructor
23451  * Create a new Item
23452  * @param {Object} config The config object
23453  */
23454
23455
23456 Roo.bootstrap.menu.Item = function(config){
23457     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23458     this.addEvents({
23459         /**
23460          * @event mouseover
23461          * Fires when the mouse is hovering over this menu
23462          * @param {Roo.bootstrap.menu.Item} this
23463          * @param {Roo.EventObject} e
23464          */
23465         mouseover : true,
23466         /**
23467          * @event mouseout
23468          * Fires when the mouse exits this menu
23469          * @param {Roo.bootstrap.menu.Item} this
23470          * @param {Roo.EventObject} e
23471          */
23472         mouseout : true,
23473         // raw events
23474         /**
23475          * @event click
23476          * The raw click event for the entire grid.
23477          * @param {Roo.EventObject} e
23478          */
23479         click : true
23480     });
23481 };
23482
23483 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23484     
23485     submenu : false,
23486     href : '',
23487     html : '',
23488     preventDefault: true,
23489     disable : false,
23490     icon : false,
23491     pos : 'right',
23492     
23493     getAutoCreate : function()
23494     {
23495         var text = [
23496             {
23497                 tag : 'span',
23498                 cls : 'roo-menu-item-text',
23499                 html : this.html
23500             }
23501         ];
23502         
23503         if(this.icon){
23504             text.unshift({
23505                 tag : 'i',
23506                 cls : 'fa ' + this.icon
23507             })
23508         }
23509         
23510         var cfg = {
23511             tag : 'li',
23512             cn : [
23513                 {
23514                     tag : 'a',
23515                     href : this.href || '#',
23516                     cn : text
23517                 }
23518             ]
23519         };
23520         
23521         if(this.disable){
23522             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23523         }
23524         
23525         if(this.submenu){
23526             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23527             
23528             if(this.pos == 'left'){
23529                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23530             }
23531         }
23532         
23533         return cfg;
23534     },
23535     
23536     initEvents : function() 
23537     {
23538         this.el.on('mouseover', this.onMouseOver, this);
23539         this.el.on('mouseout', this.onMouseOut, this);
23540         
23541         this.el.select('a', true).first().on('click', this.onClick, this);
23542         
23543     },
23544     
23545     onClick : function(e)
23546     {
23547         if(this.preventDefault){
23548             e.preventDefault();
23549         }
23550         
23551         this.fireEvent("click", this, e);
23552     },
23553     
23554     onMouseOver : function(e)
23555     {
23556         if(this.submenu && this.pos == 'left'){
23557             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23558         }
23559         
23560         this.fireEvent("mouseover", this, e);
23561     },
23562     
23563     onMouseOut : function(e)
23564     {
23565         this.fireEvent("mouseout", this, e);
23566     }
23567 });
23568
23569  
23570
23571  /*
23572  * - LGPL
23573  *
23574  * menu separator
23575  * 
23576  */
23577 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23578
23579 /**
23580  * @class Roo.bootstrap.menu.Separator
23581  * @extends Roo.bootstrap.Component
23582  * Bootstrap Separator class
23583  * 
23584  * @constructor
23585  * Create a new Separator
23586  * @param {Object} config The config object
23587  */
23588
23589
23590 Roo.bootstrap.menu.Separator = function(config){
23591     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23592 };
23593
23594 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23595     
23596     getAutoCreate : function(){
23597         var cfg = {
23598             tag : 'li',
23599             cls: 'divider'
23600         };
23601         
23602         return cfg;
23603     }
23604    
23605 });
23606
23607  
23608
23609  /*
23610  * - LGPL
23611  *
23612  * Tooltip
23613  * 
23614  */
23615
23616 /**
23617  * @class Roo.bootstrap.Tooltip
23618  * Bootstrap Tooltip class
23619  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23620  * to determine which dom element triggers the tooltip.
23621  * 
23622  * It needs to add support for additional attributes like tooltip-position
23623  * 
23624  * @constructor
23625  * Create a new Toolti
23626  * @param {Object} config The config object
23627  */
23628
23629 Roo.bootstrap.Tooltip = function(config){
23630     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23631 };
23632
23633 Roo.apply(Roo.bootstrap.Tooltip, {
23634     /**
23635      * @function init initialize tooltip monitoring.
23636      * @static
23637      */
23638     currentEl : false,
23639     currentTip : false,
23640     currentRegion : false,
23641     
23642     //  init : delay?
23643     
23644     init : function()
23645     {
23646         Roo.get(document).on('mouseover', this.enter ,this);
23647         Roo.get(document).on('mouseout', this.leave, this);
23648          
23649         
23650         this.currentTip = new Roo.bootstrap.Tooltip();
23651     },
23652     
23653     enter : function(ev)
23654     {
23655         var dom = ev.getTarget();
23656         
23657         //Roo.log(['enter',dom]);
23658         var el = Roo.fly(dom);
23659         if (this.currentEl) {
23660             //Roo.log(dom);
23661             //Roo.log(this.currentEl);
23662             //Roo.log(this.currentEl.contains(dom));
23663             if (this.currentEl == el) {
23664                 return;
23665             }
23666             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23667                 return;
23668             }
23669
23670         }
23671         
23672         if (this.currentTip.el) {
23673             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23674         }    
23675         //Roo.log(ev);
23676         var bindEl = el;
23677         
23678         // you can not look for children, as if el is the body.. then everythign is the child..
23679         if (!el.attr('tooltip')) { //
23680             if (!el.select("[tooltip]").elements.length) {
23681                 return;
23682             }
23683             // is the mouse over this child...?
23684             bindEl = el.select("[tooltip]").first();
23685             var xy = ev.getXY();
23686             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23687                 //Roo.log("not in region.");
23688                 return;
23689             }
23690             //Roo.log("child element over..");
23691             
23692         }
23693         this.currentEl = bindEl;
23694         this.currentTip.bind(bindEl);
23695         this.currentRegion = Roo.lib.Region.getRegion(dom);
23696         this.currentTip.enter();
23697         
23698     },
23699     leave : function(ev)
23700     {
23701         var dom = ev.getTarget();
23702         //Roo.log(['leave',dom]);
23703         if (!this.currentEl) {
23704             return;
23705         }
23706         
23707         
23708         if (dom != this.currentEl.dom) {
23709             return;
23710         }
23711         var xy = ev.getXY();
23712         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23713             return;
23714         }
23715         // only activate leave if mouse cursor is outside... bounding box..
23716         
23717         
23718         
23719         
23720         if (this.currentTip) {
23721             this.currentTip.leave();
23722         }
23723         //Roo.log('clear currentEl');
23724         this.currentEl = false;
23725         
23726         
23727     },
23728     alignment : {
23729         'left' : ['r-l', [-2,0], 'right'],
23730         'right' : ['l-r', [2,0], 'left'],
23731         'bottom' : ['t-b', [0,2], 'top'],
23732         'top' : [ 'b-t', [0,-2], 'bottom']
23733     }
23734     
23735 });
23736
23737
23738 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23739     
23740     
23741     bindEl : false,
23742     
23743     delay : null, // can be { show : 300 , hide: 500}
23744     
23745     timeout : null,
23746     
23747     hoverState : null, //???
23748     
23749     placement : 'bottom', 
23750     
23751     getAutoCreate : function(){
23752     
23753         var cfg = {
23754            cls : 'tooltip',
23755            role : 'tooltip',
23756            cn : [
23757                 {
23758                     cls : 'tooltip-arrow'
23759                 },
23760                 {
23761                     cls : 'tooltip-inner'
23762                 }
23763            ]
23764         };
23765         
23766         return cfg;
23767     },
23768     bind : function(el)
23769     {
23770         this.bindEl = el;
23771     },
23772       
23773     
23774     enter : function () {
23775        
23776         if (this.timeout != null) {
23777             clearTimeout(this.timeout);
23778         }
23779         
23780         this.hoverState = 'in';
23781          //Roo.log("enter - show");
23782         if (!this.delay || !this.delay.show) {
23783             this.show();
23784             return;
23785         }
23786         var _t = this;
23787         this.timeout = setTimeout(function () {
23788             if (_t.hoverState == 'in') {
23789                 _t.show();
23790             }
23791         }, this.delay.show);
23792     },
23793     leave : function()
23794     {
23795         clearTimeout(this.timeout);
23796     
23797         this.hoverState = 'out';
23798          if (!this.delay || !this.delay.hide) {
23799             this.hide();
23800             return;
23801         }
23802        
23803         var _t = this;
23804         this.timeout = setTimeout(function () {
23805             //Roo.log("leave - timeout");
23806             
23807             if (_t.hoverState == 'out') {
23808                 _t.hide();
23809                 Roo.bootstrap.Tooltip.currentEl = false;
23810             }
23811         }, delay);
23812     },
23813     
23814     show : function ()
23815     {
23816         if (!this.el) {
23817             this.render(document.body);
23818         }
23819         // set content.
23820         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23821         
23822         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23823         
23824         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23825         
23826         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23827         
23828         var placement = typeof this.placement == 'function' ?
23829             this.placement.call(this, this.el, on_el) :
23830             this.placement;
23831             
23832         var autoToken = /\s?auto?\s?/i;
23833         var autoPlace = autoToken.test(placement);
23834         if (autoPlace) {
23835             placement = placement.replace(autoToken, '') || 'top';
23836         }
23837         
23838         //this.el.detach()
23839         //this.el.setXY([0,0]);
23840         this.el.show();
23841         //this.el.dom.style.display='block';
23842         
23843         //this.el.appendTo(on_el);
23844         
23845         var p = this.getPosition();
23846         var box = this.el.getBox();
23847         
23848         if (autoPlace) {
23849             // fixme..
23850         }
23851         
23852         var align = Roo.bootstrap.Tooltip.alignment[placement];
23853         
23854         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23855         
23856         if(placement == 'top' || placement == 'bottom'){
23857             if(xy[0] < 0){
23858                 placement = 'right';
23859             }
23860             
23861             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23862                 placement = 'left';
23863             }
23864         }
23865         
23866         align = Roo.bootstrap.Tooltip.alignment[placement];
23867         
23868         this.el.alignTo(this.bindEl, align[0],align[1]);
23869         //var arrow = this.el.select('.arrow',true).first();
23870         //arrow.set(align[2], 
23871         
23872         this.el.addClass(placement);
23873         
23874         this.el.addClass('in fade');
23875         
23876         this.hoverState = null;
23877         
23878         if (this.el.hasClass('fade')) {
23879             // fade it?
23880         }
23881         
23882     },
23883     hide : function()
23884     {
23885          
23886         if (!this.el) {
23887             return;
23888         }
23889         //this.el.setXY([0,0]);
23890         this.el.removeClass('in');
23891         //this.el.hide();
23892         
23893     }
23894     
23895 });
23896  
23897
23898  /*
23899  * - LGPL
23900  *
23901  * Location Picker
23902  * 
23903  */
23904
23905 /**
23906  * @class Roo.bootstrap.LocationPicker
23907  * @extends Roo.bootstrap.Component
23908  * Bootstrap LocationPicker class
23909  * @cfg {Number} latitude Position when init default 0
23910  * @cfg {Number} longitude Position when init default 0
23911  * @cfg {Number} zoom default 15
23912  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23913  * @cfg {Boolean} mapTypeControl default false
23914  * @cfg {Boolean} disableDoubleClickZoom default false
23915  * @cfg {Boolean} scrollwheel default true
23916  * @cfg {Boolean} streetViewControl default false
23917  * @cfg {Number} radius default 0
23918  * @cfg {String} locationName
23919  * @cfg {Boolean} draggable default true
23920  * @cfg {Boolean} enableAutocomplete default false
23921  * @cfg {Boolean} enableReverseGeocode default true
23922  * @cfg {String} markerTitle
23923  * 
23924  * @constructor
23925  * Create a new LocationPicker
23926  * @param {Object} config The config object
23927  */
23928
23929
23930 Roo.bootstrap.LocationPicker = function(config){
23931     
23932     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23933     
23934     this.addEvents({
23935         /**
23936          * @event initial
23937          * Fires when the picker initialized.
23938          * @param {Roo.bootstrap.LocationPicker} this
23939          * @param {Google Location} location
23940          */
23941         initial : true,
23942         /**
23943          * @event positionchanged
23944          * Fires when the picker position changed.
23945          * @param {Roo.bootstrap.LocationPicker} this
23946          * @param {Google Location} location
23947          */
23948         positionchanged : true,
23949         /**
23950          * @event resize
23951          * Fires when the map resize.
23952          * @param {Roo.bootstrap.LocationPicker} this
23953          */
23954         resize : true,
23955         /**
23956          * @event show
23957          * Fires when the map show.
23958          * @param {Roo.bootstrap.LocationPicker} this
23959          */
23960         show : true,
23961         /**
23962          * @event hide
23963          * Fires when the map hide.
23964          * @param {Roo.bootstrap.LocationPicker} this
23965          */
23966         hide : true,
23967         /**
23968          * @event mapClick
23969          * Fires when click the map.
23970          * @param {Roo.bootstrap.LocationPicker} this
23971          * @param {Map event} e
23972          */
23973         mapClick : true,
23974         /**
23975          * @event mapRightClick
23976          * Fires when right click the map.
23977          * @param {Roo.bootstrap.LocationPicker} this
23978          * @param {Map event} e
23979          */
23980         mapRightClick : true,
23981         /**
23982          * @event markerClick
23983          * Fires when click the marker.
23984          * @param {Roo.bootstrap.LocationPicker} this
23985          * @param {Map event} e
23986          */
23987         markerClick : true,
23988         /**
23989          * @event markerRightClick
23990          * Fires when right click the marker.
23991          * @param {Roo.bootstrap.LocationPicker} this
23992          * @param {Map event} e
23993          */
23994         markerRightClick : true,
23995         /**
23996          * @event OverlayViewDraw
23997          * Fires when OverlayView Draw
23998          * @param {Roo.bootstrap.LocationPicker} this
23999          */
24000         OverlayViewDraw : true,
24001         /**
24002          * @event OverlayViewOnAdd
24003          * Fires when OverlayView Draw
24004          * @param {Roo.bootstrap.LocationPicker} this
24005          */
24006         OverlayViewOnAdd : true,
24007         /**
24008          * @event OverlayViewOnRemove
24009          * Fires when OverlayView Draw
24010          * @param {Roo.bootstrap.LocationPicker} this
24011          */
24012         OverlayViewOnRemove : true,
24013         /**
24014          * @event OverlayViewShow
24015          * Fires when OverlayView Draw
24016          * @param {Roo.bootstrap.LocationPicker} this
24017          * @param {Pixel} cpx
24018          */
24019         OverlayViewShow : true,
24020         /**
24021          * @event OverlayViewHide
24022          * Fires when OverlayView Draw
24023          * @param {Roo.bootstrap.LocationPicker} this
24024          */
24025         OverlayViewHide : true,
24026         /**
24027          * @event loadexception
24028          * Fires when load google lib failed.
24029          * @param {Roo.bootstrap.LocationPicker} this
24030          */
24031         loadexception : true
24032     });
24033         
24034 };
24035
24036 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24037     
24038     gMapContext: false,
24039     
24040     latitude: 0,
24041     longitude: 0,
24042     zoom: 15,
24043     mapTypeId: false,
24044     mapTypeControl: false,
24045     disableDoubleClickZoom: false,
24046     scrollwheel: true,
24047     streetViewControl: false,
24048     radius: 0,
24049     locationName: '',
24050     draggable: true,
24051     enableAutocomplete: false,
24052     enableReverseGeocode: true,
24053     markerTitle: '',
24054     
24055     getAutoCreate: function()
24056     {
24057
24058         var cfg = {
24059             tag: 'div',
24060             cls: 'roo-location-picker'
24061         };
24062         
24063         return cfg
24064     },
24065     
24066     initEvents: function(ct, position)
24067     {       
24068         if(!this.el.getWidth() || this.isApplied()){
24069             return;
24070         }
24071         
24072         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24073         
24074         this.initial();
24075     },
24076     
24077     initial: function()
24078     {
24079         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24080             this.fireEvent('loadexception', this);
24081             return;
24082         }
24083         
24084         if(!this.mapTypeId){
24085             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24086         }
24087         
24088         this.gMapContext = this.GMapContext();
24089         
24090         this.initOverlayView();
24091         
24092         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24093         
24094         var _this = this;
24095                 
24096         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24097             _this.setPosition(_this.gMapContext.marker.position);
24098         });
24099         
24100         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24101             _this.fireEvent('mapClick', this, event);
24102             
24103         });
24104
24105         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24106             _this.fireEvent('mapRightClick', this, event);
24107             
24108         });
24109         
24110         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24111             _this.fireEvent('markerClick', this, event);
24112             
24113         });
24114
24115         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24116             _this.fireEvent('markerRightClick', this, event);
24117             
24118         });
24119         
24120         this.setPosition(this.gMapContext.location);
24121         
24122         this.fireEvent('initial', this, this.gMapContext.location);
24123     },
24124     
24125     initOverlayView: function()
24126     {
24127         var _this = this;
24128         
24129         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24130             
24131             draw: function()
24132             {
24133                 _this.fireEvent('OverlayViewDraw', _this);
24134             },
24135             
24136             onAdd: function()
24137             {
24138                 _this.fireEvent('OverlayViewOnAdd', _this);
24139             },
24140             
24141             onRemove: function()
24142             {
24143                 _this.fireEvent('OverlayViewOnRemove', _this);
24144             },
24145             
24146             show: function(cpx)
24147             {
24148                 _this.fireEvent('OverlayViewShow', _this, cpx);
24149             },
24150             
24151             hide: function()
24152             {
24153                 _this.fireEvent('OverlayViewHide', _this);
24154             }
24155             
24156         });
24157     },
24158     
24159     fromLatLngToContainerPixel: function(event)
24160     {
24161         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24162     },
24163     
24164     isApplied: function() 
24165     {
24166         return this.getGmapContext() == false ? false : true;
24167     },
24168     
24169     getGmapContext: function() 
24170     {
24171         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24172     },
24173     
24174     GMapContext: function() 
24175     {
24176         var position = new google.maps.LatLng(this.latitude, this.longitude);
24177         
24178         var _map = new google.maps.Map(this.el.dom, {
24179             center: position,
24180             zoom: this.zoom,
24181             mapTypeId: this.mapTypeId,
24182             mapTypeControl: this.mapTypeControl,
24183             disableDoubleClickZoom: this.disableDoubleClickZoom,
24184             scrollwheel: this.scrollwheel,
24185             streetViewControl: this.streetViewControl,
24186             locationName: this.locationName,
24187             draggable: this.draggable,
24188             enableAutocomplete: this.enableAutocomplete,
24189             enableReverseGeocode: this.enableReverseGeocode
24190         });
24191         
24192         var _marker = new google.maps.Marker({
24193             position: position,
24194             map: _map,
24195             title: this.markerTitle,
24196             draggable: this.draggable
24197         });
24198         
24199         return {
24200             map: _map,
24201             marker: _marker,
24202             circle: null,
24203             location: position,
24204             radius: this.radius,
24205             locationName: this.locationName,
24206             addressComponents: {
24207                 formatted_address: null,
24208                 addressLine1: null,
24209                 addressLine2: null,
24210                 streetName: null,
24211                 streetNumber: null,
24212                 city: null,
24213                 district: null,
24214                 state: null,
24215                 stateOrProvince: null
24216             },
24217             settings: this,
24218             domContainer: this.el.dom,
24219             geodecoder: new google.maps.Geocoder()
24220         };
24221     },
24222     
24223     drawCircle: function(center, radius, options) 
24224     {
24225         if (this.gMapContext.circle != null) {
24226             this.gMapContext.circle.setMap(null);
24227         }
24228         if (radius > 0) {
24229             radius *= 1;
24230             options = Roo.apply({}, options, {
24231                 strokeColor: "#0000FF",
24232                 strokeOpacity: .35,
24233                 strokeWeight: 2,
24234                 fillColor: "#0000FF",
24235                 fillOpacity: .2
24236             });
24237             
24238             options.map = this.gMapContext.map;
24239             options.radius = radius;
24240             options.center = center;
24241             this.gMapContext.circle = new google.maps.Circle(options);
24242             return this.gMapContext.circle;
24243         }
24244         
24245         return null;
24246     },
24247     
24248     setPosition: function(location) 
24249     {
24250         this.gMapContext.location = location;
24251         this.gMapContext.marker.setPosition(location);
24252         this.gMapContext.map.panTo(location);
24253         this.drawCircle(location, this.gMapContext.radius, {});
24254         
24255         var _this = this;
24256         
24257         if (this.gMapContext.settings.enableReverseGeocode) {
24258             this.gMapContext.geodecoder.geocode({
24259                 latLng: this.gMapContext.location
24260             }, function(results, status) {
24261                 
24262                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24263                     _this.gMapContext.locationName = results[0].formatted_address;
24264                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24265                     
24266                     _this.fireEvent('positionchanged', this, location);
24267                 }
24268             });
24269             
24270             return;
24271         }
24272         
24273         this.fireEvent('positionchanged', this, location);
24274     },
24275     
24276     resize: function()
24277     {
24278         google.maps.event.trigger(this.gMapContext.map, "resize");
24279         
24280         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24281         
24282         this.fireEvent('resize', this);
24283     },
24284     
24285     setPositionByLatLng: function(latitude, longitude)
24286     {
24287         this.setPosition(new google.maps.LatLng(latitude, longitude));
24288     },
24289     
24290     getCurrentPosition: function() 
24291     {
24292         return {
24293             latitude: this.gMapContext.location.lat(),
24294             longitude: this.gMapContext.location.lng()
24295         };
24296     },
24297     
24298     getAddressName: function() 
24299     {
24300         return this.gMapContext.locationName;
24301     },
24302     
24303     getAddressComponents: function() 
24304     {
24305         return this.gMapContext.addressComponents;
24306     },
24307     
24308     address_component_from_google_geocode: function(address_components) 
24309     {
24310         var result = {};
24311         
24312         for (var i = 0; i < address_components.length; i++) {
24313             var component = address_components[i];
24314             if (component.types.indexOf("postal_code") >= 0) {
24315                 result.postalCode = component.short_name;
24316             } else if (component.types.indexOf("street_number") >= 0) {
24317                 result.streetNumber = component.short_name;
24318             } else if (component.types.indexOf("route") >= 0) {
24319                 result.streetName = component.short_name;
24320             } else if (component.types.indexOf("neighborhood") >= 0) {
24321                 result.city = component.short_name;
24322             } else if (component.types.indexOf("locality") >= 0) {
24323                 result.city = component.short_name;
24324             } else if (component.types.indexOf("sublocality") >= 0) {
24325                 result.district = component.short_name;
24326             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24327                 result.stateOrProvince = component.short_name;
24328             } else if (component.types.indexOf("country") >= 0) {
24329                 result.country = component.short_name;
24330             }
24331         }
24332         
24333         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24334         result.addressLine2 = "";
24335         return result;
24336     },
24337     
24338     setZoomLevel: function(zoom)
24339     {
24340         this.gMapContext.map.setZoom(zoom);
24341     },
24342     
24343     show: function()
24344     {
24345         if(!this.el){
24346             return;
24347         }
24348         
24349         this.el.show();
24350         
24351         this.resize();
24352         
24353         this.fireEvent('show', this);
24354     },
24355     
24356     hide: function()
24357     {
24358         if(!this.el){
24359             return;
24360         }
24361         
24362         this.el.hide();
24363         
24364         this.fireEvent('hide', this);
24365     }
24366     
24367 });
24368
24369 Roo.apply(Roo.bootstrap.LocationPicker, {
24370     
24371     OverlayView : function(map, options)
24372     {
24373         options = options || {};
24374         
24375         this.setMap(map);
24376     }
24377     
24378     
24379 });/*
24380  * - LGPL
24381  *
24382  * Alert
24383  * 
24384  */
24385
24386 /**
24387  * @class Roo.bootstrap.Alert
24388  * @extends Roo.bootstrap.Component
24389  * Bootstrap Alert class
24390  * @cfg {String} title The title of alert
24391  * @cfg {String} html The content of alert
24392  * @cfg {String} weight (  success | info | warning | danger )
24393  * @cfg {String} faicon font-awesomeicon
24394  * 
24395  * @constructor
24396  * Create a new alert
24397  * @param {Object} config The config object
24398  */
24399
24400
24401 Roo.bootstrap.Alert = function(config){
24402     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24403     
24404 };
24405
24406 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24407     
24408     title: '',
24409     html: '',
24410     weight: false,
24411     faicon: false,
24412     
24413     getAutoCreate : function()
24414     {
24415         
24416         var cfg = {
24417             tag : 'div',
24418             cls : 'alert',
24419             cn : [
24420                 {
24421                     tag : 'i',
24422                     cls : 'roo-alert-icon'
24423                     
24424                 },
24425                 {
24426                     tag : 'b',
24427                     cls : 'roo-alert-title',
24428                     html : this.title
24429                 },
24430                 {
24431                     tag : 'span',
24432                     cls : 'roo-alert-text',
24433                     html : this.html
24434                 }
24435             ]
24436         };
24437         
24438         if(this.faicon){
24439             cfg.cn[0].cls += ' fa ' + this.faicon;
24440         }
24441         
24442         if(this.weight){
24443             cfg.cls += ' alert-' + this.weight;
24444         }
24445         
24446         return cfg;
24447     },
24448     
24449     initEvents: function() 
24450     {
24451         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24452     },
24453     
24454     setTitle : function(str)
24455     {
24456         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24457     },
24458     
24459     setText : function(str)
24460     {
24461         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24462     },
24463     
24464     setWeight : function(weight)
24465     {
24466         if(this.weight){
24467             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24468         }
24469         
24470         this.weight = weight;
24471         
24472         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24473     },
24474     
24475     setIcon : function(icon)
24476     {
24477         if(this.faicon){
24478             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24479         }
24480         
24481         this.faicon = icon;
24482         
24483         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24484     },
24485     
24486     hide: function() 
24487     {
24488         this.el.hide();   
24489     },
24490     
24491     show: function() 
24492     {  
24493         this.el.show();   
24494     }
24495     
24496 });
24497
24498  
24499 /*
24500 * Licence: LGPL
24501 */
24502
24503 /**
24504  * @class Roo.bootstrap.UploadCropbox
24505  * @extends Roo.bootstrap.Component
24506  * Bootstrap UploadCropbox class
24507  * @cfg {String} emptyText show when image has been loaded
24508  * @cfg {String} rotateNotify show when image too small to rotate
24509  * @cfg {Number} errorTimeout default 3000
24510  * @cfg {Number} minWidth default 300
24511  * @cfg {Number} minHeight default 300
24512  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24513  * @cfg {Boolean} isDocument (true|false) default false
24514  * @cfg {String} url action url
24515  * @cfg {String} paramName default 'imageUpload'
24516  * @cfg {String} method default POST
24517  * @cfg {Boolean} loadMask (true|false) default true
24518  * @cfg {Boolean} loadingText default 'Loading...'
24519  * 
24520  * @constructor
24521  * Create a new UploadCropbox
24522  * @param {Object} config The config object
24523  */
24524
24525 Roo.bootstrap.UploadCropbox = function(config){
24526     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24527     
24528     this.addEvents({
24529         /**
24530          * @event beforeselectfile
24531          * Fire before select file
24532          * @param {Roo.bootstrap.UploadCropbox} this
24533          */
24534         "beforeselectfile" : true,
24535         /**
24536          * @event initial
24537          * Fire after initEvent
24538          * @param {Roo.bootstrap.UploadCropbox} this
24539          */
24540         "initial" : true,
24541         /**
24542          * @event crop
24543          * Fire after initEvent
24544          * @param {Roo.bootstrap.UploadCropbox} this
24545          * @param {String} data
24546          */
24547         "crop" : true,
24548         /**
24549          * @event prepare
24550          * Fire when preparing the file data
24551          * @param {Roo.bootstrap.UploadCropbox} this
24552          * @param {Object} file
24553          */
24554         "prepare" : true,
24555         /**
24556          * @event exception
24557          * Fire when get exception
24558          * @param {Roo.bootstrap.UploadCropbox} this
24559          * @param {XMLHttpRequest} xhr
24560          */
24561         "exception" : true,
24562         /**
24563          * @event beforeloadcanvas
24564          * Fire before load the canvas
24565          * @param {Roo.bootstrap.UploadCropbox} this
24566          * @param {String} src
24567          */
24568         "beforeloadcanvas" : true,
24569         /**
24570          * @event trash
24571          * Fire when trash image
24572          * @param {Roo.bootstrap.UploadCropbox} this
24573          */
24574         "trash" : true,
24575         /**
24576          * @event download
24577          * Fire when download the image
24578          * @param {Roo.bootstrap.UploadCropbox} this
24579          */
24580         "download" : true,
24581         /**
24582          * @event footerbuttonclick
24583          * Fire when footerbuttonclick
24584          * @param {Roo.bootstrap.UploadCropbox} this
24585          * @param {String} type
24586          */
24587         "footerbuttonclick" : true,
24588         /**
24589          * @event resize
24590          * Fire when resize
24591          * @param {Roo.bootstrap.UploadCropbox} this
24592          */
24593         "resize" : true,
24594         /**
24595          * @event rotate
24596          * Fire when rotate the image
24597          * @param {Roo.bootstrap.UploadCropbox} this
24598          * @param {String} pos
24599          */
24600         "rotate" : true,
24601         /**
24602          * @event inspect
24603          * Fire when inspect the file
24604          * @param {Roo.bootstrap.UploadCropbox} this
24605          * @param {Object} file
24606          */
24607         "inspect" : true,
24608         /**
24609          * @event upload
24610          * Fire when xhr upload the file
24611          * @param {Roo.bootstrap.UploadCropbox} this
24612          * @param {Object} data
24613          */
24614         "upload" : true,
24615         /**
24616          * @event arrange
24617          * Fire when arrange the file data
24618          * @param {Roo.bootstrap.UploadCropbox} this
24619          * @param {Object} formData
24620          */
24621         "arrange" : true
24622     });
24623     
24624     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24625 };
24626
24627 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24628     
24629     emptyText : 'Click to upload image',
24630     rotateNotify : 'Image is too small to rotate',
24631     errorTimeout : 3000,
24632     scale : 0,
24633     baseScale : 1,
24634     rotate : 0,
24635     dragable : false,
24636     pinching : false,
24637     mouseX : 0,
24638     mouseY : 0,
24639     cropData : false,
24640     minWidth : 300,
24641     minHeight : 300,
24642     file : false,
24643     exif : {},
24644     baseRotate : 1,
24645     cropType : 'image/jpeg',
24646     buttons : false,
24647     canvasLoaded : false,
24648     isDocument : false,
24649     method : 'POST',
24650     paramName : 'imageUpload',
24651     loadMask : true,
24652     loadingText : 'Loading...',
24653     maskEl : false,
24654     
24655     getAutoCreate : function()
24656     {
24657         var cfg = {
24658             tag : 'div',
24659             cls : 'roo-upload-cropbox',
24660             cn : [
24661                 {
24662                     tag : 'input',
24663                     cls : 'roo-upload-cropbox-selector',
24664                     type : 'file'
24665                 },
24666                 {
24667                     tag : 'div',
24668                     cls : 'roo-upload-cropbox-body',
24669                     style : 'cursor:pointer',
24670                     cn : [
24671                         {
24672                             tag : 'div',
24673                             cls : 'roo-upload-cropbox-preview'
24674                         },
24675                         {
24676                             tag : 'div',
24677                             cls : 'roo-upload-cropbox-thumb'
24678                         },
24679                         {
24680                             tag : 'div',
24681                             cls : 'roo-upload-cropbox-empty-notify',
24682                             html : this.emptyText
24683                         },
24684                         {
24685                             tag : 'div',
24686                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24687                             html : this.rotateNotify
24688                         }
24689                     ]
24690                 },
24691                 {
24692                     tag : 'div',
24693                     cls : 'roo-upload-cropbox-footer',
24694                     cn : {
24695                         tag : 'div',
24696                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24697                         cn : []
24698                     }
24699                 }
24700             ]
24701         };
24702         
24703         return cfg;
24704     },
24705     
24706     onRender : function(ct, position)
24707     {
24708         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24709         
24710         if (this.buttons.length) {
24711             
24712             Roo.each(this.buttons, function(bb) {
24713                 
24714                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24715                 
24716                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24717                 
24718             }, this);
24719         }
24720         
24721         if(this.loadMask){
24722             this.maskEl = this.el;
24723         }
24724     },
24725     
24726     initEvents : function()
24727     {
24728         this.urlAPI = (window.createObjectURL && window) || 
24729                                 (window.URL && URL.revokeObjectURL && URL) || 
24730                                 (window.webkitURL && webkitURL);
24731                         
24732         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24733         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24734         
24735         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24736         this.selectorEl.hide();
24737         
24738         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24739         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24740         
24741         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24742         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24743         this.thumbEl.hide();
24744         
24745         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24746         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24747         
24748         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24749         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24750         this.errorEl.hide();
24751         
24752         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24753         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24754         this.footerEl.hide();
24755         
24756         this.setThumbBoxSize();
24757         
24758         this.bind();
24759         
24760         this.resize();
24761         
24762         this.fireEvent('initial', this);
24763     },
24764
24765     bind : function()
24766     {
24767         var _this = this;
24768         
24769         window.addEventListener("resize", function() { _this.resize(); } );
24770         
24771         this.bodyEl.on('click', this.beforeSelectFile, this);
24772         
24773         if(Roo.isTouch){
24774             this.bodyEl.on('touchstart', this.onTouchStart, this);
24775             this.bodyEl.on('touchmove', this.onTouchMove, this);
24776             this.bodyEl.on('touchend', this.onTouchEnd, this);
24777         }
24778         
24779         if(!Roo.isTouch){
24780             this.bodyEl.on('mousedown', this.onMouseDown, this);
24781             this.bodyEl.on('mousemove', this.onMouseMove, this);
24782             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24783             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24784             Roo.get(document).on('mouseup', this.onMouseUp, this);
24785         }
24786         
24787         this.selectorEl.on('change', this.onFileSelected, this);
24788     },
24789     
24790     reset : function()
24791     {    
24792         this.scale = 0;
24793         this.baseScale = 1;
24794         this.rotate = 0;
24795         this.baseRotate = 1;
24796         this.dragable = false;
24797         this.pinching = false;
24798         this.mouseX = 0;
24799         this.mouseY = 0;
24800         this.cropData = false;
24801         this.notifyEl.dom.innerHTML = this.emptyText;
24802         
24803         this.selectorEl.dom.value = '';
24804         
24805     },
24806     
24807     resize : function()
24808     {
24809         if(this.fireEvent('resize', this) != false){
24810             this.setThumbBoxPosition();
24811             this.setCanvasPosition();
24812         }
24813     },
24814     
24815     onFooterButtonClick : function(e, el, o, type)
24816     {
24817         switch (type) {
24818             case 'rotate-left' :
24819                 this.onRotateLeft(e);
24820                 break;
24821             case 'rotate-right' :
24822                 this.onRotateRight(e);
24823                 break;
24824             case 'picture' :
24825                 this.beforeSelectFile(e);
24826                 break;
24827             case 'trash' :
24828                 this.trash(e);
24829                 break;
24830             case 'crop' :
24831                 this.crop(e);
24832                 break;
24833             case 'download' :
24834                 this.download(e);
24835                 break;
24836             default :
24837                 break;
24838         }
24839         
24840         this.fireEvent('footerbuttonclick', this, type);
24841     },
24842     
24843     beforeSelectFile : function(e)
24844     {
24845         e.preventDefault();
24846         
24847         if(this.fireEvent('beforeselectfile', this) != false){
24848             this.selectorEl.dom.click();
24849         }
24850     },
24851     
24852     onFileSelected : function(e)
24853     {
24854         e.preventDefault();
24855         
24856         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24857             return;
24858         }
24859         
24860         var file = this.selectorEl.dom.files[0];
24861         
24862         if(this.fireEvent('inspect', this, file) != false){
24863             this.prepare(file);
24864         }
24865         
24866     },
24867     
24868     trash : function(e)
24869     {
24870         this.fireEvent('trash', this);
24871     },
24872     
24873     download : function(e)
24874     {
24875         this.fireEvent('download', this);
24876     },
24877     
24878     loadCanvas : function(src)
24879     {   
24880         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24881             
24882             this.reset();
24883             
24884             this.imageEl = document.createElement('img');
24885             
24886             var _this = this;
24887             
24888             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24889             
24890             this.imageEl.src = src;
24891         }
24892     },
24893     
24894     onLoadCanvas : function()
24895     {   
24896         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24897         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24898         
24899         this.bodyEl.un('click', this.beforeSelectFile, this);
24900         
24901         this.notifyEl.hide();
24902         this.thumbEl.show();
24903         this.footerEl.show();
24904         
24905         this.baseRotateLevel();
24906         
24907         if(this.isDocument){
24908             this.setThumbBoxSize();
24909         }
24910         
24911         this.setThumbBoxPosition();
24912         
24913         this.baseScaleLevel();
24914         
24915         this.draw();
24916         
24917         this.resize();
24918         
24919         this.canvasLoaded = true;
24920         
24921         if(this.loadMask){
24922             this.maskEl.unmask();
24923         }
24924         
24925     },
24926     
24927     setCanvasPosition : function()
24928     {   
24929         if(!this.canvasEl){
24930             return;
24931         }
24932         
24933         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24934         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24935         
24936         this.previewEl.setLeft(pw);
24937         this.previewEl.setTop(ph);
24938         
24939     },
24940     
24941     onMouseDown : function(e)
24942     {   
24943         e.stopEvent();
24944         
24945         this.dragable = true;
24946         this.pinching = false;
24947         
24948         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24949             this.dragable = false;
24950             return;
24951         }
24952         
24953         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24954         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24955         
24956     },
24957     
24958     onMouseMove : function(e)
24959     {   
24960         e.stopEvent();
24961         
24962         if(!this.canvasLoaded){
24963             return;
24964         }
24965         
24966         if (!this.dragable){
24967             return;
24968         }
24969         
24970         var minX = Math.ceil(this.thumbEl.getLeft(true));
24971         var minY = Math.ceil(this.thumbEl.getTop(true));
24972         
24973         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24974         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24975         
24976         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24977         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24978         
24979         x = x - this.mouseX;
24980         y = y - this.mouseY;
24981         
24982         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24983         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24984         
24985         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24986         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24987         
24988         this.previewEl.setLeft(bgX);
24989         this.previewEl.setTop(bgY);
24990         
24991         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24992         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24993     },
24994     
24995     onMouseUp : function(e)
24996     {   
24997         e.stopEvent();
24998         
24999         this.dragable = false;
25000     },
25001     
25002     onMouseWheel : function(e)
25003     {   
25004         e.stopEvent();
25005         
25006         this.startScale = this.scale;
25007         
25008         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25009         
25010         if(!this.zoomable()){
25011             this.scale = this.startScale;
25012             return;
25013         }
25014         
25015         this.draw();
25016         
25017         return;
25018     },
25019     
25020     zoomable : function()
25021     {
25022         var minScale = this.thumbEl.getWidth() / this.minWidth;
25023         
25024         if(this.minWidth < this.minHeight){
25025             minScale = this.thumbEl.getHeight() / this.minHeight;
25026         }
25027         
25028         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25029         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25030         
25031         if(
25032                 this.isDocument &&
25033                 (this.rotate == 0 || this.rotate == 180) && 
25034                 (
25035                     width > this.imageEl.OriginWidth || 
25036                     height > this.imageEl.OriginHeight ||
25037                     (width < this.minWidth && height < this.minHeight)
25038                 )
25039         ){
25040             return false;
25041         }
25042         
25043         if(
25044                 this.isDocument &&
25045                 (this.rotate == 90 || this.rotate == 270) && 
25046                 (
25047                     width > this.imageEl.OriginWidth || 
25048                     height > this.imageEl.OriginHeight ||
25049                     (width < this.minHeight && height < this.minWidth)
25050                 )
25051         ){
25052             return false;
25053         }
25054         
25055         if(
25056                 !this.isDocument &&
25057                 (this.rotate == 0 || this.rotate == 180) && 
25058                 (
25059                     width < this.minWidth || 
25060                     width > this.imageEl.OriginWidth || 
25061                     height < this.minHeight || 
25062                     height > this.imageEl.OriginHeight
25063                 )
25064         ){
25065             return false;
25066         }
25067         
25068         if(
25069                 !this.isDocument &&
25070                 (this.rotate == 90 || this.rotate == 270) && 
25071                 (
25072                     width < this.minHeight || 
25073                     width > this.imageEl.OriginWidth || 
25074                     height < this.minWidth || 
25075                     height > this.imageEl.OriginHeight
25076                 )
25077         ){
25078             return false;
25079         }
25080         
25081         return true;
25082         
25083     },
25084     
25085     onRotateLeft : function(e)
25086     {   
25087         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25088             
25089             var minScale = this.thumbEl.getWidth() / this.minWidth;
25090             
25091             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25092             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25093             
25094             this.startScale = this.scale;
25095             
25096             while (this.getScaleLevel() < minScale){
25097             
25098                 this.scale = this.scale + 1;
25099                 
25100                 if(!this.zoomable()){
25101                     break;
25102                 }
25103                 
25104                 if(
25105                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25106                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25107                 ){
25108                     continue;
25109                 }
25110                 
25111                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25112
25113                 this.draw();
25114                 
25115                 return;
25116             }
25117             
25118             this.scale = this.startScale;
25119             
25120             this.onRotateFail();
25121             
25122             return false;
25123         }
25124         
25125         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25126
25127         if(this.isDocument){
25128             this.setThumbBoxSize();
25129             this.setThumbBoxPosition();
25130             this.setCanvasPosition();
25131         }
25132         
25133         this.draw();
25134         
25135         this.fireEvent('rotate', this, 'left');
25136         
25137     },
25138     
25139     onRotateRight : function(e)
25140     {
25141         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25142             
25143             var minScale = this.thumbEl.getWidth() / this.minWidth;
25144         
25145             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25146             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25147             
25148             this.startScale = this.scale;
25149             
25150             while (this.getScaleLevel() < minScale){
25151             
25152                 this.scale = this.scale + 1;
25153                 
25154                 if(!this.zoomable()){
25155                     break;
25156                 }
25157                 
25158                 if(
25159                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25160                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25161                 ){
25162                     continue;
25163                 }
25164                 
25165                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25166
25167                 this.draw();
25168                 
25169                 return;
25170             }
25171             
25172             this.scale = this.startScale;
25173             
25174             this.onRotateFail();
25175             
25176             return false;
25177         }
25178         
25179         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25180
25181         if(this.isDocument){
25182             this.setThumbBoxSize();
25183             this.setThumbBoxPosition();
25184             this.setCanvasPosition();
25185         }
25186         
25187         this.draw();
25188         
25189         this.fireEvent('rotate', this, 'right');
25190     },
25191     
25192     onRotateFail : function()
25193     {
25194         this.errorEl.show(true);
25195         
25196         var _this = this;
25197         
25198         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25199     },
25200     
25201     draw : function()
25202     {
25203         this.previewEl.dom.innerHTML = '';
25204         
25205         var canvasEl = document.createElement("canvas");
25206         
25207         var contextEl = canvasEl.getContext("2d");
25208         
25209         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25210         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25211         var center = this.imageEl.OriginWidth / 2;
25212         
25213         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25214             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25215             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25216             center = this.imageEl.OriginHeight / 2;
25217         }
25218         
25219         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25220         
25221         contextEl.translate(center, center);
25222         contextEl.rotate(this.rotate * Math.PI / 180);
25223
25224         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25225         
25226         this.canvasEl = document.createElement("canvas");
25227         
25228         this.contextEl = this.canvasEl.getContext("2d");
25229         
25230         switch (this.rotate) {
25231             case 0 :
25232                 
25233                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25234                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25235                 
25236                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25237                 
25238                 break;
25239             case 90 : 
25240                 
25241                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25242                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25243                 
25244                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25245                     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);
25246                     break;
25247                 }
25248                 
25249                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25250                 
25251                 break;
25252             case 180 :
25253                 
25254                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25255                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25256                 
25257                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25258                     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);
25259                     break;
25260                 }
25261                 
25262                 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);
25263                 
25264                 break;
25265             case 270 :
25266                 
25267                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25268                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25269         
25270                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25271                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25272                     break;
25273                 }
25274                 
25275                 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);
25276                 
25277                 break;
25278             default : 
25279                 break;
25280         }
25281         
25282         this.previewEl.appendChild(this.canvasEl);
25283         
25284         this.setCanvasPosition();
25285     },
25286     
25287     crop : function()
25288     {
25289         if(!this.canvasLoaded){
25290             return;
25291         }
25292         
25293         var imageCanvas = document.createElement("canvas");
25294         
25295         var imageContext = imageCanvas.getContext("2d");
25296         
25297         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25298         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25299         
25300         var center = imageCanvas.width / 2;
25301         
25302         imageContext.translate(center, center);
25303         
25304         imageContext.rotate(this.rotate * Math.PI / 180);
25305         
25306         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25307         
25308         var canvas = document.createElement("canvas");
25309         
25310         var context = canvas.getContext("2d");
25311                 
25312         canvas.width = this.minWidth;
25313         canvas.height = this.minHeight;
25314
25315         switch (this.rotate) {
25316             case 0 :
25317                 
25318                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25319                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25320                 
25321                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25322                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25323                 
25324                 var targetWidth = this.minWidth - 2 * x;
25325                 var targetHeight = this.minHeight - 2 * y;
25326                 
25327                 var scale = 1;
25328                 
25329                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25330                     scale = targetWidth / width;
25331                 }
25332                 
25333                 if(x > 0 && y == 0){
25334                     scale = targetHeight / height;
25335                 }
25336                 
25337                 if(x > 0 && y > 0){
25338                     scale = targetWidth / width;
25339                     
25340                     if(width < height){
25341                         scale = targetHeight / height;
25342                     }
25343                 }
25344                 
25345                 context.scale(scale, scale);
25346                 
25347                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25348                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25349
25350                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25351                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25352
25353                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25354                 
25355                 break;
25356             case 90 : 
25357                 
25358                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25359                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25360                 
25361                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25362                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25363                 
25364                 var targetWidth = this.minWidth - 2 * x;
25365                 var targetHeight = this.minHeight - 2 * y;
25366                 
25367                 var scale = 1;
25368                 
25369                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25370                     scale = targetWidth / width;
25371                 }
25372                 
25373                 if(x > 0 && y == 0){
25374                     scale = targetHeight / height;
25375                 }
25376                 
25377                 if(x > 0 && y > 0){
25378                     scale = targetWidth / width;
25379                     
25380                     if(width < height){
25381                         scale = targetHeight / height;
25382                     }
25383                 }
25384                 
25385                 context.scale(scale, scale);
25386                 
25387                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25388                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25389
25390                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25391                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25392                 
25393                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25394                 
25395                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25396                 
25397                 break;
25398             case 180 :
25399                 
25400                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25401                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25402                 
25403                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25404                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25405                 
25406                 var targetWidth = this.minWidth - 2 * x;
25407                 var targetHeight = this.minHeight - 2 * y;
25408                 
25409                 var scale = 1;
25410                 
25411                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25412                     scale = targetWidth / width;
25413                 }
25414                 
25415                 if(x > 0 && y == 0){
25416                     scale = targetHeight / height;
25417                 }
25418                 
25419                 if(x > 0 && y > 0){
25420                     scale = targetWidth / width;
25421                     
25422                     if(width < height){
25423                         scale = targetHeight / height;
25424                     }
25425                 }
25426                 
25427                 context.scale(scale, scale);
25428                 
25429                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25430                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25431
25432                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25433                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25434
25435                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25436                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25437                 
25438                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25439                 
25440                 break;
25441             case 270 :
25442                 
25443                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25444                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25445                 
25446                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25447                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25448                 
25449                 var targetWidth = this.minWidth - 2 * x;
25450                 var targetHeight = this.minHeight - 2 * y;
25451                 
25452                 var scale = 1;
25453                 
25454                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25455                     scale = targetWidth / width;
25456                 }
25457                 
25458                 if(x > 0 && y == 0){
25459                     scale = targetHeight / height;
25460                 }
25461                 
25462                 if(x > 0 && y > 0){
25463                     scale = targetWidth / width;
25464                     
25465                     if(width < height){
25466                         scale = targetHeight / height;
25467                     }
25468                 }
25469                 
25470                 context.scale(scale, scale);
25471                 
25472                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25473                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25474
25475                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25476                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25477                 
25478                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25479                 
25480                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25481                 
25482                 break;
25483             default : 
25484                 break;
25485         }
25486         
25487         this.cropData = canvas.toDataURL(this.cropType);
25488         
25489         if(this.fireEvent('crop', this, this.cropData) !== false){
25490             this.process(this.file, this.cropData);
25491         }
25492         
25493         return;
25494         
25495     },
25496     
25497     setThumbBoxSize : function()
25498     {
25499         var width, height;
25500         
25501         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25502             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25503             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25504             
25505             this.minWidth = width;
25506             this.minHeight = height;
25507             
25508             if(this.rotate == 90 || this.rotate == 270){
25509                 this.minWidth = height;
25510                 this.minHeight = width;
25511             }
25512         }
25513         
25514         height = 300;
25515         width = Math.ceil(this.minWidth * height / this.minHeight);
25516         
25517         if(this.minWidth > this.minHeight){
25518             width = 300;
25519             height = Math.ceil(this.minHeight * width / this.minWidth);
25520         }
25521         
25522         this.thumbEl.setStyle({
25523             width : width + 'px',
25524             height : height + 'px'
25525         });
25526
25527         return;
25528             
25529     },
25530     
25531     setThumbBoxPosition : function()
25532     {
25533         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25534         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25535         
25536         this.thumbEl.setLeft(x);
25537         this.thumbEl.setTop(y);
25538         
25539     },
25540     
25541     baseRotateLevel : function()
25542     {
25543         this.baseRotate = 1;
25544         
25545         if(
25546                 typeof(this.exif) != 'undefined' &&
25547                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25548                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25549         ){
25550             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25551         }
25552         
25553         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25554         
25555     },
25556     
25557     baseScaleLevel : function()
25558     {
25559         var width, height;
25560         
25561         if(this.isDocument){
25562             
25563             if(this.baseRotate == 6 || this.baseRotate == 8){
25564             
25565                 height = this.thumbEl.getHeight();
25566                 this.baseScale = height / this.imageEl.OriginWidth;
25567
25568                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25569                     width = this.thumbEl.getWidth();
25570                     this.baseScale = width / this.imageEl.OriginHeight;
25571                 }
25572
25573                 return;
25574             }
25575
25576             height = this.thumbEl.getHeight();
25577             this.baseScale = height / this.imageEl.OriginHeight;
25578
25579             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25580                 width = this.thumbEl.getWidth();
25581                 this.baseScale = width / this.imageEl.OriginWidth;
25582             }
25583
25584             return;
25585         }
25586         
25587         if(this.baseRotate == 6 || this.baseRotate == 8){
25588             
25589             width = this.thumbEl.getHeight();
25590             this.baseScale = width / this.imageEl.OriginHeight;
25591             
25592             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25593                 height = this.thumbEl.getWidth();
25594                 this.baseScale = height / this.imageEl.OriginHeight;
25595             }
25596             
25597             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25598                 height = this.thumbEl.getWidth();
25599                 this.baseScale = height / this.imageEl.OriginHeight;
25600                 
25601                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25602                     width = this.thumbEl.getHeight();
25603                     this.baseScale = width / this.imageEl.OriginWidth;
25604                 }
25605             }
25606             
25607             return;
25608         }
25609         
25610         width = this.thumbEl.getWidth();
25611         this.baseScale = width / this.imageEl.OriginWidth;
25612         
25613         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25614             height = this.thumbEl.getHeight();
25615             this.baseScale = height / this.imageEl.OriginHeight;
25616         }
25617         
25618         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25619             
25620             height = this.thumbEl.getHeight();
25621             this.baseScale = height / this.imageEl.OriginHeight;
25622             
25623             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25624                 width = this.thumbEl.getWidth();
25625                 this.baseScale = width / this.imageEl.OriginWidth;
25626             }
25627             
25628         }
25629         
25630         return;
25631     },
25632     
25633     getScaleLevel : function()
25634     {
25635         return this.baseScale * Math.pow(1.1, this.scale);
25636     },
25637     
25638     onTouchStart : function(e)
25639     {
25640         if(!this.canvasLoaded){
25641             this.beforeSelectFile(e);
25642             return;
25643         }
25644         
25645         var touches = e.browserEvent.touches;
25646         
25647         if(!touches){
25648             return;
25649         }
25650         
25651         if(touches.length == 1){
25652             this.onMouseDown(e);
25653             return;
25654         }
25655         
25656         if(touches.length != 2){
25657             return;
25658         }
25659         
25660         var coords = [];
25661         
25662         for(var i = 0, finger; finger = touches[i]; i++){
25663             coords.push(finger.pageX, finger.pageY);
25664         }
25665         
25666         var x = Math.pow(coords[0] - coords[2], 2);
25667         var y = Math.pow(coords[1] - coords[3], 2);
25668         
25669         this.startDistance = Math.sqrt(x + y);
25670         
25671         this.startScale = this.scale;
25672         
25673         this.pinching = true;
25674         this.dragable = false;
25675         
25676     },
25677     
25678     onTouchMove : function(e)
25679     {
25680         if(!this.pinching && !this.dragable){
25681             return;
25682         }
25683         
25684         var touches = e.browserEvent.touches;
25685         
25686         if(!touches){
25687             return;
25688         }
25689         
25690         if(this.dragable){
25691             this.onMouseMove(e);
25692             return;
25693         }
25694         
25695         var coords = [];
25696         
25697         for(var i = 0, finger; finger = touches[i]; i++){
25698             coords.push(finger.pageX, finger.pageY);
25699         }
25700         
25701         var x = Math.pow(coords[0] - coords[2], 2);
25702         var y = Math.pow(coords[1] - coords[3], 2);
25703         
25704         this.endDistance = Math.sqrt(x + y);
25705         
25706         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25707         
25708         if(!this.zoomable()){
25709             this.scale = this.startScale;
25710             return;
25711         }
25712         
25713         this.draw();
25714         
25715     },
25716     
25717     onTouchEnd : function(e)
25718     {
25719         this.pinching = false;
25720         this.dragable = false;
25721         
25722     },
25723     
25724     process : function(file, crop)
25725     {
25726         if(this.loadMask){
25727             this.maskEl.mask(this.loadingText);
25728         }
25729         
25730         this.xhr = new XMLHttpRequest();
25731         
25732         file.xhr = this.xhr;
25733
25734         this.xhr.open(this.method, this.url, true);
25735         
25736         var headers = {
25737             "Accept": "application/json",
25738             "Cache-Control": "no-cache",
25739             "X-Requested-With": "XMLHttpRequest"
25740         };
25741         
25742         for (var headerName in headers) {
25743             var headerValue = headers[headerName];
25744             if (headerValue) {
25745                 this.xhr.setRequestHeader(headerName, headerValue);
25746             }
25747         }
25748         
25749         var _this = this;
25750         
25751         this.xhr.onload = function()
25752         {
25753             _this.xhrOnLoad(_this.xhr);
25754         }
25755         
25756         this.xhr.onerror = function()
25757         {
25758             _this.xhrOnError(_this.xhr);
25759         }
25760         
25761         var formData = new FormData();
25762
25763         formData.append('returnHTML', 'NO');
25764         
25765         if(crop){
25766             formData.append('crop', crop);
25767         }
25768         
25769         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25770             formData.append(this.paramName, file, file.name);
25771         }
25772         
25773         if(typeof(file.filename) != 'undefined'){
25774             formData.append('filename', file.filename);
25775         }
25776         
25777         if(typeof(file.mimetype) != 'undefined'){
25778             formData.append('mimetype', file.mimetype);
25779         }
25780         
25781         if(this.fireEvent('arrange', this, formData) != false){
25782             this.xhr.send(formData);
25783         };
25784     },
25785     
25786     xhrOnLoad : function(xhr)
25787     {
25788         if(this.loadMask){
25789             this.maskEl.unmask();
25790         }
25791         
25792         if (xhr.readyState !== 4) {
25793             this.fireEvent('exception', this, xhr);
25794             return;
25795         }
25796
25797         var response = Roo.decode(xhr.responseText);
25798         
25799         if(!response.success){
25800             this.fireEvent('exception', this, xhr);
25801             return;
25802         }
25803         
25804         var response = Roo.decode(xhr.responseText);
25805         
25806         this.fireEvent('upload', this, response);
25807         
25808     },
25809     
25810     xhrOnError : function()
25811     {
25812         if(this.loadMask){
25813             this.maskEl.unmask();
25814         }
25815         
25816         Roo.log('xhr on error');
25817         
25818         var response = Roo.decode(xhr.responseText);
25819           
25820         Roo.log(response);
25821         
25822     },
25823     
25824     prepare : function(file)
25825     {   
25826         if(this.loadMask){
25827             this.maskEl.mask(this.loadingText);
25828         }
25829         
25830         this.file = false;
25831         this.exif = {};
25832         
25833         if(typeof(file) === 'string'){
25834             this.loadCanvas(file);
25835             return;
25836         }
25837         
25838         if(!file || !this.urlAPI){
25839             return;
25840         }
25841         
25842         this.file = file;
25843         this.cropType = file.type;
25844         
25845         var _this = this;
25846         
25847         if(this.fireEvent('prepare', this, this.file) != false){
25848             
25849             var reader = new FileReader();
25850             
25851             reader.onload = function (e) {
25852                 if (e.target.error) {
25853                     Roo.log(e.target.error);
25854                     return;
25855                 }
25856                 
25857                 var buffer = e.target.result,
25858                     dataView = new DataView(buffer),
25859                     offset = 2,
25860                     maxOffset = dataView.byteLength - 4,
25861                     markerBytes,
25862                     markerLength;
25863                 
25864                 if (dataView.getUint16(0) === 0xffd8) {
25865                     while (offset < maxOffset) {
25866                         markerBytes = dataView.getUint16(offset);
25867                         
25868                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25869                             markerLength = dataView.getUint16(offset + 2) + 2;
25870                             if (offset + markerLength > dataView.byteLength) {
25871                                 Roo.log('Invalid meta data: Invalid segment size.');
25872                                 break;
25873                             }
25874                             
25875                             if(markerBytes == 0xffe1){
25876                                 _this.parseExifData(
25877                                     dataView,
25878                                     offset,
25879                                     markerLength
25880                                 );
25881                             }
25882                             
25883                             offset += markerLength;
25884                             
25885                             continue;
25886                         }
25887                         
25888                         break;
25889                     }
25890                     
25891                 }
25892                 
25893                 var url = _this.urlAPI.createObjectURL(_this.file);
25894                 
25895                 _this.loadCanvas(url);
25896                 
25897                 return;
25898             }
25899             
25900             reader.readAsArrayBuffer(this.file);
25901             
25902         }
25903         
25904     },
25905     
25906     parseExifData : function(dataView, offset, length)
25907     {
25908         var tiffOffset = offset + 10,
25909             littleEndian,
25910             dirOffset;
25911     
25912         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25913             // No Exif data, might be XMP data instead
25914             return;
25915         }
25916         
25917         // Check for the ASCII code for "Exif" (0x45786966):
25918         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25919             // No Exif data, might be XMP data instead
25920             return;
25921         }
25922         if (tiffOffset + 8 > dataView.byteLength) {
25923             Roo.log('Invalid Exif data: Invalid segment size.');
25924             return;
25925         }
25926         // Check for the two null bytes:
25927         if (dataView.getUint16(offset + 8) !== 0x0000) {
25928             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25929             return;
25930         }
25931         // Check the byte alignment:
25932         switch (dataView.getUint16(tiffOffset)) {
25933         case 0x4949:
25934             littleEndian = true;
25935             break;
25936         case 0x4D4D:
25937             littleEndian = false;
25938             break;
25939         default:
25940             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25941             return;
25942         }
25943         // Check for the TIFF tag marker (0x002A):
25944         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25945             Roo.log('Invalid Exif data: Missing TIFF marker.');
25946             return;
25947         }
25948         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25949         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25950         
25951         this.parseExifTags(
25952             dataView,
25953             tiffOffset,
25954             tiffOffset + dirOffset,
25955             littleEndian
25956         );
25957     },
25958     
25959     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25960     {
25961         var tagsNumber,
25962             dirEndOffset,
25963             i;
25964         if (dirOffset + 6 > dataView.byteLength) {
25965             Roo.log('Invalid Exif data: Invalid directory offset.');
25966             return;
25967         }
25968         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25969         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25970         if (dirEndOffset + 4 > dataView.byteLength) {
25971             Roo.log('Invalid Exif data: Invalid directory size.');
25972             return;
25973         }
25974         for (i = 0; i < tagsNumber; i += 1) {
25975             this.parseExifTag(
25976                 dataView,
25977                 tiffOffset,
25978                 dirOffset + 2 + 12 * i, // tag offset
25979                 littleEndian
25980             );
25981         }
25982         // Return the offset to the next directory:
25983         return dataView.getUint32(dirEndOffset, littleEndian);
25984     },
25985     
25986     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25987     {
25988         var tag = dataView.getUint16(offset, littleEndian);
25989         
25990         this.exif[tag] = this.getExifValue(
25991             dataView,
25992             tiffOffset,
25993             offset,
25994             dataView.getUint16(offset + 2, littleEndian), // tag type
25995             dataView.getUint32(offset + 4, littleEndian), // tag length
25996             littleEndian
25997         );
25998     },
25999     
26000     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26001     {
26002         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26003             tagSize,
26004             dataOffset,
26005             values,
26006             i,
26007             str,
26008             c;
26009     
26010         if (!tagType) {
26011             Roo.log('Invalid Exif data: Invalid tag type.');
26012             return;
26013         }
26014         
26015         tagSize = tagType.size * length;
26016         // Determine if the value is contained in the dataOffset bytes,
26017         // or if the value at the dataOffset is a pointer to the actual data:
26018         dataOffset = tagSize > 4 ?
26019                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26020         if (dataOffset + tagSize > dataView.byteLength) {
26021             Roo.log('Invalid Exif data: Invalid data offset.');
26022             return;
26023         }
26024         if (length === 1) {
26025             return tagType.getValue(dataView, dataOffset, littleEndian);
26026         }
26027         values = [];
26028         for (i = 0; i < length; i += 1) {
26029             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26030         }
26031         
26032         if (tagType.ascii) {
26033             str = '';
26034             // Concatenate the chars:
26035             for (i = 0; i < values.length; i += 1) {
26036                 c = values[i];
26037                 // Ignore the terminating NULL byte(s):
26038                 if (c === '\u0000') {
26039                     break;
26040                 }
26041                 str += c;
26042             }
26043             return str;
26044         }
26045         return values;
26046     }
26047     
26048 });
26049
26050 Roo.apply(Roo.bootstrap.UploadCropbox, {
26051     tags : {
26052         'Orientation': 0x0112
26053     },
26054     
26055     Orientation: {
26056             1: 0, //'top-left',
26057 //            2: 'top-right',
26058             3: 180, //'bottom-right',
26059 //            4: 'bottom-left',
26060 //            5: 'left-top',
26061             6: 90, //'right-top',
26062 //            7: 'right-bottom',
26063             8: 270 //'left-bottom'
26064     },
26065     
26066     exifTagTypes : {
26067         // byte, 8-bit unsigned int:
26068         1: {
26069             getValue: function (dataView, dataOffset) {
26070                 return dataView.getUint8(dataOffset);
26071             },
26072             size: 1
26073         },
26074         // ascii, 8-bit byte:
26075         2: {
26076             getValue: function (dataView, dataOffset) {
26077                 return String.fromCharCode(dataView.getUint8(dataOffset));
26078             },
26079             size: 1,
26080             ascii: true
26081         },
26082         // short, 16 bit int:
26083         3: {
26084             getValue: function (dataView, dataOffset, littleEndian) {
26085                 return dataView.getUint16(dataOffset, littleEndian);
26086             },
26087             size: 2
26088         },
26089         // long, 32 bit int:
26090         4: {
26091             getValue: function (dataView, dataOffset, littleEndian) {
26092                 return dataView.getUint32(dataOffset, littleEndian);
26093             },
26094             size: 4
26095         },
26096         // rational = two long values, first is numerator, second is denominator:
26097         5: {
26098             getValue: function (dataView, dataOffset, littleEndian) {
26099                 return dataView.getUint32(dataOffset, littleEndian) /
26100                     dataView.getUint32(dataOffset + 4, littleEndian);
26101             },
26102             size: 8
26103         },
26104         // slong, 32 bit signed int:
26105         9: {
26106             getValue: function (dataView, dataOffset, littleEndian) {
26107                 return dataView.getInt32(dataOffset, littleEndian);
26108             },
26109             size: 4
26110         },
26111         // srational, two slongs, first is numerator, second is denominator:
26112         10: {
26113             getValue: function (dataView, dataOffset, littleEndian) {
26114                 return dataView.getInt32(dataOffset, littleEndian) /
26115                     dataView.getInt32(dataOffset + 4, littleEndian);
26116             },
26117             size: 8
26118         }
26119     },
26120     
26121     footer : {
26122         STANDARD : [
26123             {
26124                 tag : 'div',
26125                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26126                 action : 'rotate-left',
26127                 cn : [
26128                     {
26129                         tag : 'button',
26130                         cls : 'btn btn-default',
26131                         html : '<i class="fa fa-undo"></i>'
26132                     }
26133                 ]
26134             },
26135             {
26136                 tag : 'div',
26137                 cls : 'btn-group roo-upload-cropbox-picture',
26138                 action : 'picture',
26139                 cn : [
26140                     {
26141                         tag : 'button',
26142                         cls : 'btn btn-default',
26143                         html : '<i class="fa fa-picture-o"></i>'
26144                     }
26145                 ]
26146             },
26147             {
26148                 tag : 'div',
26149                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26150                 action : 'rotate-right',
26151                 cn : [
26152                     {
26153                         tag : 'button',
26154                         cls : 'btn btn-default',
26155                         html : '<i class="fa fa-repeat"></i>'
26156                     }
26157                 ]
26158             }
26159         ],
26160         DOCUMENT : [
26161             {
26162                 tag : 'div',
26163                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26164                 action : 'rotate-left',
26165                 cn : [
26166                     {
26167                         tag : 'button',
26168                         cls : 'btn btn-default',
26169                         html : '<i class="fa fa-undo"></i>'
26170                     }
26171                 ]
26172             },
26173             {
26174                 tag : 'div',
26175                 cls : 'btn-group roo-upload-cropbox-download',
26176                 action : 'download',
26177                 cn : [
26178                     {
26179                         tag : 'button',
26180                         cls : 'btn btn-default',
26181                         html : '<i class="fa fa-download"></i>'
26182                     }
26183                 ]
26184             },
26185             {
26186                 tag : 'div',
26187                 cls : 'btn-group roo-upload-cropbox-crop',
26188                 action : 'crop',
26189                 cn : [
26190                     {
26191                         tag : 'button',
26192                         cls : 'btn btn-default',
26193                         html : '<i class="fa fa-crop"></i>'
26194                     }
26195                 ]
26196             },
26197             {
26198                 tag : 'div',
26199                 cls : 'btn-group roo-upload-cropbox-trash',
26200                 action : 'trash',
26201                 cn : [
26202                     {
26203                         tag : 'button',
26204                         cls : 'btn btn-default',
26205                         html : '<i class="fa fa-trash"></i>'
26206                     }
26207                 ]
26208             },
26209             {
26210                 tag : 'div',
26211                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26212                 action : 'rotate-right',
26213                 cn : [
26214                     {
26215                         tag : 'button',
26216                         cls : 'btn btn-default',
26217                         html : '<i class="fa fa-repeat"></i>'
26218                     }
26219                 ]
26220             }
26221         ],
26222         ROTATOR : [
26223             {
26224                 tag : 'div',
26225                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26226                 action : 'rotate-left',
26227                 cn : [
26228                     {
26229                         tag : 'button',
26230                         cls : 'btn btn-default',
26231                         html : '<i class="fa fa-undo"></i>'
26232                     }
26233                 ]
26234             },
26235             {
26236                 tag : 'div',
26237                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26238                 action : 'rotate-right',
26239                 cn : [
26240                     {
26241                         tag : 'button',
26242                         cls : 'btn btn-default',
26243                         html : '<i class="fa fa-repeat"></i>'
26244                     }
26245                 ]
26246             }
26247         ]
26248     }
26249 });
26250
26251 /*
26252 * Licence: LGPL
26253 */
26254
26255 /**
26256  * @class Roo.bootstrap.DocumentManager
26257  * @extends Roo.bootstrap.Component
26258  * Bootstrap DocumentManager class
26259  * @cfg {String} paramName default 'imageUpload'
26260  * @cfg {String} method default POST
26261  * @cfg {String} url action url
26262  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26263  * @cfg {Boolean} multiple multiple upload default true
26264  * @cfg {Number} thumbSize default 300
26265  * @cfg {String} fieldLabel
26266  * @cfg {Number} labelWidth default 4
26267  * @cfg {String} labelAlign (left|top) default left
26268  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26269  * 
26270  * @constructor
26271  * Create a new DocumentManager
26272  * @param {Object} config The config object
26273  */
26274
26275 Roo.bootstrap.DocumentManager = function(config){
26276     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26277     
26278     this.addEvents({
26279         /**
26280          * @event initial
26281          * Fire when initial the DocumentManager
26282          * @param {Roo.bootstrap.DocumentManager} this
26283          */
26284         "initial" : true,
26285         /**
26286          * @event inspect
26287          * inspect selected file
26288          * @param {Roo.bootstrap.DocumentManager} this
26289          * @param {File} file
26290          */
26291         "inspect" : true,
26292         /**
26293          * @event exception
26294          * Fire when xhr load exception
26295          * @param {Roo.bootstrap.DocumentManager} this
26296          * @param {XMLHttpRequest} xhr
26297          */
26298         "exception" : true,
26299         /**
26300          * @event prepare
26301          * prepare the form data
26302          * @param {Roo.bootstrap.DocumentManager} this
26303          * @param {Object} formData
26304          */
26305         "prepare" : true,
26306         /**
26307          * @event remove
26308          * Fire when remove the file
26309          * @param {Roo.bootstrap.DocumentManager} this
26310          * @param {Object} file
26311          */
26312         "remove" : true,
26313         /**
26314          * @event refresh
26315          * Fire after refresh the file
26316          * @param {Roo.bootstrap.DocumentManager} this
26317          */
26318         "refresh" : true,
26319         /**
26320          * @event click
26321          * Fire after click the image
26322          * @param {Roo.bootstrap.DocumentManager} this
26323          * @param {Object} file
26324          */
26325         "click" : true,
26326         /**
26327          * @event edit
26328          * Fire when upload a image and editable set to true
26329          * @param {Roo.bootstrap.DocumentManager} this
26330          * @param {Object} file
26331          */
26332         "edit" : true,
26333         /**
26334          * @event beforeselectfile
26335          * Fire before select file
26336          * @param {Roo.bootstrap.DocumentManager} this
26337          */
26338         "beforeselectfile" : true,
26339         /**
26340          * @event process
26341          * Fire before process file
26342          * @param {Roo.bootstrap.DocumentManager} this
26343          * @param {Object} file
26344          */
26345         "process" : true
26346         
26347     });
26348 };
26349
26350 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26351     
26352     boxes : 0,
26353     inputName : '',
26354     thumbSize : 300,
26355     multiple : true,
26356     files : [],
26357     method : 'POST',
26358     url : '',
26359     paramName : 'imageUpload',
26360     fieldLabel : '',
26361     labelWidth : 4,
26362     labelAlign : 'left',
26363     editable : true,
26364     delegates : [],
26365     
26366     
26367     xhr : false, 
26368     
26369     getAutoCreate : function()
26370     {   
26371         var managerWidget = {
26372             tag : 'div',
26373             cls : 'roo-document-manager',
26374             cn : [
26375                 {
26376                     tag : 'input',
26377                     cls : 'roo-document-manager-selector',
26378                     type : 'file'
26379                 },
26380                 {
26381                     tag : 'div',
26382                     cls : 'roo-document-manager-uploader',
26383                     cn : [
26384                         {
26385                             tag : 'div',
26386                             cls : 'roo-document-manager-upload-btn',
26387                             html : '<i class="fa fa-plus"></i>'
26388                         }
26389                     ]
26390                     
26391                 }
26392             ]
26393         };
26394         
26395         var content = [
26396             {
26397                 tag : 'div',
26398                 cls : 'column col-md-12',
26399                 cn : managerWidget
26400             }
26401         ];
26402         
26403         if(this.fieldLabel.length){
26404             
26405             content = [
26406                 {
26407                     tag : 'div',
26408                     cls : 'column col-md-12',
26409                     html : this.fieldLabel
26410                 },
26411                 {
26412                     tag : 'div',
26413                     cls : 'column col-md-12',
26414                     cn : managerWidget
26415                 }
26416             ];
26417
26418             if(this.labelAlign == 'left'){
26419                 content = [
26420                     {
26421                         tag : 'div',
26422                         cls : 'column col-md-' + this.labelWidth,
26423                         html : this.fieldLabel
26424                     },
26425                     {
26426                         tag : 'div',
26427                         cls : 'column col-md-' + (12 - this.labelWidth),
26428                         cn : managerWidget
26429                     }
26430                 ];
26431                 
26432             }
26433         }
26434         
26435         var cfg = {
26436             tag : 'div',
26437             cls : 'row clearfix',
26438             cn : content
26439         };
26440         
26441         return cfg;
26442         
26443     },
26444     
26445     initEvents : function()
26446     {
26447         this.managerEl = this.el.select('.roo-document-manager', true).first();
26448         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26449         
26450         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26451         this.selectorEl.hide();
26452         
26453         if(this.multiple){
26454             this.selectorEl.attr('multiple', 'multiple');
26455         }
26456         
26457         this.selectorEl.on('change', this.onFileSelected, this);
26458         
26459         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26460         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26461         
26462         this.uploader.on('click', this.onUploaderClick, this);
26463         
26464         this.renderProgressDialog();
26465         
26466         var _this = this;
26467         
26468         window.addEventListener("resize", function() { _this.refresh(); } );
26469         
26470         this.fireEvent('initial', this);
26471     },
26472     
26473     renderProgressDialog : function()
26474     {
26475         var _this = this;
26476         
26477         this.progressDialog = new Roo.bootstrap.Modal({
26478             cls : 'roo-document-manager-progress-dialog',
26479             allow_close : false,
26480             title : '',
26481             buttons : [
26482                 {
26483                     name  :'cancel',
26484                     weight : 'danger',
26485                     html : 'Cancel'
26486                 }
26487             ], 
26488             listeners : { 
26489                 btnclick : function() {
26490                     _this.uploadCancel();
26491                     this.hide();
26492                 }
26493             }
26494         });
26495          
26496         this.progressDialog.render(Roo.get(document.body));
26497          
26498         this.progress = new Roo.bootstrap.Progress({
26499             cls : 'roo-document-manager-progress',
26500             active : true,
26501             striped : true
26502         });
26503         
26504         this.progress.render(this.progressDialog.getChildContainer());
26505         
26506         this.progressBar = new Roo.bootstrap.ProgressBar({
26507             cls : 'roo-document-manager-progress-bar',
26508             aria_valuenow : 0,
26509             aria_valuemin : 0,
26510             aria_valuemax : 12,
26511             panel : 'success'
26512         });
26513         
26514         this.progressBar.render(this.progress.getChildContainer());
26515     },
26516     
26517     onUploaderClick : function(e)
26518     {
26519         e.preventDefault();
26520      
26521         if(this.fireEvent('beforeselectfile', this) != false){
26522             this.selectorEl.dom.click();
26523         }
26524         
26525     },
26526     
26527     onFileSelected : function(e)
26528     {
26529         e.preventDefault();
26530         
26531         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26532             return;
26533         }
26534         
26535         Roo.each(this.selectorEl.dom.files, function(file){
26536             if(this.fireEvent('inspect', this, file) != false){
26537                 this.files.push(file);
26538             }
26539         }, this);
26540         
26541         this.queue();
26542         
26543     },
26544     
26545     queue : function()
26546     {
26547         this.selectorEl.dom.value = '';
26548         
26549         if(!this.files.length){
26550             return;
26551         }
26552         
26553         if(this.boxes > 0 && this.files.length > this.boxes){
26554             this.files = this.files.slice(0, this.boxes);
26555         }
26556         
26557         this.uploader.show();
26558         
26559         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26560             this.uploader.hide();
26561         }
26562         
26563         var _this = this;
26564         
26565         var files = [];
26566         
26567         var docs = [];
26568         
26569         Roo.each(this.files, function(file){
26570             
26571             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26572                 var f = this.renderPreview(file);
26573                 files.push(f);
26574                 return;
26575             }
26576             
26577             if(file.type.indexOf('image') != -1){
26578                 this.delegates.push(
26579                     (function(){
26580                         _this.process(file);
26581                     }).createDelegate(this)
26582                 );
26583         
26584                 return;
26585             }
26586             
26587             docs.push(
26588                 (function(){
26589                     _this.process(file);
26590                 }).createDelegate(this)
26591             );
26592             
26593         }, this);
26594         
26595         this.files = files;
26596         
26597         this.delegates = this.delegates.concat(docs);
26598         
26599         if(!this.delegates.length){
26600             this.refresh();
26601             return;
26602         }
26603         
26604         this.progressBar.aria_valuemax = this.delegates.length;
26605         
26606         this.arrange();
26607         
26608         return;
26609     },
26610     
26611     arrange : function()
26612     {
26613         if(!this.delegates.length){
26614             this.progressDialog.hide();
26615             this.refresh();
26616             return;
26617         }
26618         
26619         var delegate = this.delegates.shift();
26620         
26621         this.progressDialog.show();
26622         
26623         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26624         
26625         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26626         
26627         delegate();
26628     },
26629     
26630     refresh : function()
26631     {
26632         this.uploader.show();
26633         
26634         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26635             this.uploader.hide();
26636         }
26637         
26638         Roo.isTouch ? this.closable(false) : this.closable(true);
26639         
26640         this.fireEvent('refresh', this);
26641     },
26642     
26643     onRemove : function(e, el, o)
26644     {
26645         e.preventDefault();
26646         
26647         this.fireEvent('remove', this, o);
26648         
26649     },
26650     
26651     remove : function(o)
26652     {
26653         var files = [];
26654         
26655         Roo.each(this.files, function(file){
26656             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26657                 files.push(file);
26658                 return;
26659             }
26660
26661             o.target.remove();
26662
26663         }, this);
26664         
26665         this.files = files;
26666         
26667         this.refresh();
26668     },
26669     
26670     clear : function()
26671     {
26672         Roo.each(this.files, function(file){
26673             if(!file.target){
26674                 return;
26675             }
26676             
26677             file.target.remove();
26678
26679         }, this);
26680         
26681         this.files = [];
26682         
26683         this.refresh();
26684     },
26685     
26686     onClick : function(e, el, o)
26687     {
26688         e.preventDefault();
26689         
26690         this.fireEvent('click', this, o);
26691         
26692     },
26693     
26694     closable : function(closable)
26695     {
26696         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26697             
26698             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26699             
26700             if(closable){
26701                 el.show();
26702                 return;
26703             }
26704             
26705             el.hide();
26706             
26707         }, this);
26708     },
26709     
26710     xhrOnLoad : function(xhr)
26711     {
26712         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26713             el.remove();
26714         }, this);
26715         
26716         if (xhr.readyState !== 4) {
26717             this.arrange();
26718             this.fireEvent('exception', this, xhr);
26719             return;
26720         }
26721
26722         var response = Roo.decode(xhr.responseText);
26723         
26724         if(!response.success){
26725             this.arrange();
26726             this.fireEvent('exception', this, xhr);
26727             return;
26728         }
26729         
26730         var file = this.renderPreview(response.data);
26731         
26732         this.files.push(file);
26733         
26734         this.arrange();
26735         
26736     },
26737     
26738     xhrOnError : function()
26739     {
26740         Roo.log('xhr on error');
26741         
26742         var response = Roo.decode(xhr.responseText);
26743           
26744         Roo.log(response);
26745         
26746         this.arrange();
26747     },
26748     
26749     process : function(file)
26750     {
26751         if(this.fireEvent('process', this, file) !== false){
26752             if(this.editable && file.type.indexOf('image') != -1){
26753                 this.fireEvent('edit', this, file);
26754                 return;
26755             }
26756
26757             this.uploadStart(file, false);
26758
26759             return;
26760         }
26761         
26762     },
26763     
26764     uploadStart : function(file, crop)
26765     {
26766         this.xhr = new XMLHttpRequest();
26767         
26768         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26769             this.arrange();
26770             return;
26771         }
26772         
26773         file.xhr = this.xhr;
26774             
26775         this.managerEl.createChild({
26776             tag : 'div',
26777             cls : 'roo-document-manager-loading',
26778             cn : [
26779                 {
26780                     tag : 'div',
26781                     tooltip : file.name,
26782                     cls : 'roo-document-manager-thumb',
26783                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26784                 }
26785             ]
26786
26787         });
26788
26789         this.xhr.open(this.method, this.url, true);
26790         
26791         var headers = {
26792             "Accept": "application/json",
26793             "Cache-Control": "no-cache",
26794             "X-Requested-With": "XMLHttpRequest"
26795         };
26796         
26797         for (var headerName in headers) {
26798             var headerValue = headers[headerName];
26799             if (headerValue) {
26800                 this.xhr.setRequestHeader(headerName, headerValue);
26801             }
26802         }
26803         
26804         var _this = this;
26805         
26806         this.xhr.onload = function()
26807         {
26808             _this.xhrOnLoad(_this.xhr);
26809         }
26810         
26811         this.xhr.onerror = function()
26812         {
26813             _this.xhrOnError(_this.xhr);
26814         }
26815         
26816         var formData = new FormData();
26817
26818         formData.append('returnHTML', 'NO');
26819         
26820         if(crop){
26821             formData.append('crop', crop);
26822         }
26823         
26824         formData.append(this.paramName, file, file.name);
26825         
26826         if(this.fireEvent('prepare', this, formData) != false){
26827             this.xhr.send(formData);
26828         };
26829     },
26830     
26831     uploadCancel : function()
26832     {
26833         if (this.xhr) {
26834             this.xhr.abort();
26835         }
26836         
26837         
26838         this.delegates = [];
26839         
26840         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26841             el.remove();
26842         }, this);
26843         
26844         this.arrange();
26845     },
26846     
26847     renderPreview : function(file)
26848     {
26849         if(typeof(file.target) != 'undefined' && file.target){
26850             return file;
26851         }
26852         
26853         var previewEl = this.managerEl.createChild({
26854             tag : 'div',
26855             cls : 'roo-document-manager-preview',
26856             cn : [
26857                 {
26858                     tag : 'div',
26859                     tooltip : file.filename,
26860                     cls : 'roo-document-manager-thumb',
26861                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26862                 },
26863                 {
26864                     tag : 'button',
26865                     cls : 'close',
26866                     html : '<i class="fa fa-times-circle"></i>'
26867                 }
26868             ]
26869         });
26870
26871         var close = previewEl.select('button.close', true).first();
26872
26873         close.on('click', this.onRemove, this, file);
26874
26875         file.target = previewEl;
26876
26877         var image = previewEl.select('img', true).first();
26878         
26879         var _this = this;
26880         
26881         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26882         
26883         image.on('click', this.onClick, this, file);
26884         
26885         return file;
26886         
26887     },
26888     
26889     onPreviewLoad : function(file, image)
26890     {
26891         if(typeof(file.target) == 'undefined' || !file.target){
26892             return;
26893         }
26894         
26895         var width = image.dom.naturalWidth || image.dom.width;
26896         var height = image.dom.naturalHeight || image.dom.height;
26897         
26898         if(width > height){
26899             file.target.addClass('wide');
26900             return;
26901         }
26902         
26903         file.target.addClass('tall');
26904         return;
26905         
26906     },
26907     
26908     uploadFromSource : function(file, crop)
26909     {
26910         this.xhr = new XMLHttpRequest();
26911         
26912         this.managerEl.createChild({
26913             tag : 'div',
26914             cls : 'roo-document-manager-loading',
26915             cn : [
26916                 {
26917                     tag : 'div',
26918                     tooltip : file.name,
26919                     cls : 'roo-document-manager-thumb',
26920                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26921                 }
26922             ]
26923
26924         });
26925
26926         this.xhr.open(this.method, this.url, true);
26927         
26928         var headers = {
26929             "Accept": "application/json",
26930             "Cache-Control": "no-cache",
26931             "X-Requested-With": "XMLHttpRequest"
26932         };
26933         
26934         for (var headerName in headers) {
26935             var headerValue = headers[headerName];
26936             if (headerValue) {
26937                 this.xhr.setRequestHeader(headerName, headerValue);
26938             }
26939         }
26940         
26941         var _this = this;
26942         
26943         this.xhr.onload = function()
26944         {
26945             _this.xhrOnLoad(_this.xhr);
26946         }
26947         
26948         this.xhr.onerror = function()
26949         {
26950             _this.xhrOnError(_this.xhr);
26951         }
26952         
26953         var formData = new FormData();
26954
26955         formData.append('returnHTML', 'NO');
26956         
26957         formData.append('crop', crop);
26958         
26959         if(typeof(file.filename) != 'undefined'){
26960             formData.append('filename', file.filename);
26961         }
26962         
26963         if(typeof(file.mimetype) != 'undefined'){
26964             formData.append('mimetype', file.mimetype);
26965         }
26966         
26967         if(this.fireEvent('prepare', this, formData) != false){
26968             this.xhr.send(formData);
26969         };
26970     }
26971 });
26972
26973 /*
26974 * Licence: LGPL
26975 */
26976
26977 /**
26978  * @class Roo.bootstrap.DocumentViewer
26979  * @extends Roo.bootstrap.Component
26980  * Bootstrap DocumentViewer class
26981  * 
26982  * @constructor
26983  * Create a new DocumentViewer
26984  * @param {Object} config The config object
26985  */
26986
26987 Roo.bootstrap.DocumentViewer = function(config){
26988     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26989     
26990     this.addEvents({
26991         /**
26992          * @event initial
26993          * Fire after initEvent
26994          * @param {Roo.bootstrap.DocumentViewer} this
26995          */
26996         "initial" : true,
26997         /**
26998          * @event click
26999          * Fire after click
27000          * @param {Roo.bootstrap.DocumentViewer} this
27001          */
27002         "click" : true,
27003         /**
27004          * @event trash
27005          * Fire after trash button
27006          * @param {Roo.bootstrap.DocumentViewer} this
27007          */
27008         "trash" : true
27009         
27010     });
27011 };
27012
27013 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27014     
27015     getAutoCreate : function()
27016     {
27017         var cfg = {
27018             tag : 'div',
27019             cls : 'roo-document-viewer',
27020             cn : [
27021                 {
27022                     tag : 'div',
27023                     cls : 'roo-document-viewer-body',
27024                     cn : [
27025                         {
27026                             tag : 'div',
27027                             cls : 'roo-document-viewer-thumb',
27028                             cn : [
27029                                 {
27030                                     tag : 'img',
27031                                     cls : 'roo-document-viewer-image'
27032                                 }
27033                             ]
27034                         }
27035                     ]
27036                 },
27037                 {
27038                     tag : 'div',
27039                     cls : 'roo-document-viewer-footer',
27040                     cn : {
27041                         tag : 'div',
27042                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27043                         cn : [
27044                             {
27045                                 tag : 'div',
27046                                 cls : 'btn-group',
27047                                 cn : [
27048                                     {
27049                                         tag : 'button',
27050                                         cls : 'btn btn-default roo-document-viewer-trash',
27051                                         html : '<i class="fa fa-trash"></i>'
27052                                     }
27053                                 ]
27054                             }
27055                         ]
27056                     }
27057                 }
27058             ]
27059         };
27060         
27061         return cfg;
27062     },
27063     
27064     initEvents : function()
27065     {
27066         
27067         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27068         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069         
27070         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27071         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072         
27073         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27074         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27075         
27076         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27077         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27078         
27079         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27080         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27081         
27082         this.bodyEl.on('click', this.onClick, this);
27083         
27084         this.trashBtn.on('click', this.onTrash, this);
27085         
27086     },
27087     
27088     initial : function()
27089     {
27090 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27091         
27092         
27093         this.fireEvent('initial', this);
27094         
27095     },
27096     
27097     onClick : function(e)
27098     {
27099         e.preventDefault();
27100         
27101         this.fireEvent('click', this);
27102     },
27103     
27104     onTrash : function(e)
27105     {
27106         e.preventDefault();
27107         
27108         this.fireEvent('trash', this);
27109     }
27110     
27111 });
27112 /*
27113  * - LGPL
27114  *
27115  * nav progress bar
27116  * 
27117  */
27118
27119 /**
27120  * @class Roo.bootstrap.NavProgressBar
27121  * @extends Roo.bootstrap.Component
27122  * Bootstrap NavProgressBar class
27123  * 
27124  * @constructor
27125  * Create a new nav progress bar
27126  * @param {Object} config The config object
27127  */
27128
27129 Roo.bootstrap.NavProgressBar = function(config){
27130     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27131
27132     this.bullets = this.bullets || [];
27133    
27134 //    Roo.bootstrap.NavProgressBar.register(this);
27135      this.addEvents({
27136         /**
27137              * @event changed
27138              * Fires when the active item changes
27139              * @param {Roo.bootstrap.NavProgressBar} this
27140              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27141              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27142          */
27143         'changed': true
27144      });
27145     
27146 };
27147
27148 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27149     
27150     bullets : [],
27151     barItems : [],
27152     
27153     getAutoCreate : function()
27154     {
27155         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27156         
27157         cfg = {
27158             tag : 'div',
27159             cls : 'roo-navigation-bar-group',
27160             cn : [
27161                 {
27162                     tag : 'div',
27163                     cls : 'roo-navigation-top-bar'
27164                 },
27165                 {
27166                     tag : 'div',
27167                     cls : 'roo-navigation-bullets-bar',
27168                     cn : [
27169                         {
27170                             tag : 'ul',
27171                             cls : 'roo-navigation-bar'
27172                         }
27173                     ]
27174                 },
27175                 
27176                 {
27177                     tag : 'div',
27178                     cls : 'roo-navigation-bottom-bar'
27179                 }
27180             ]
27181             
27182         };
27183         
27184         return cfg;
27185         
27186     },
27187     
27188     initEvents: function() 
27189     {
27190         
27191     },
27192     
27193     onRender : function(ct, position) 
27194     {
27195         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27196         
27197         if(this.bullets.length){
27198             Roo.each(this.bullets, function(b){
27199                this.addItem(b);
27200             }, this);
27201         }
27202         
27203         this.format();
27204         
27205     },
27206     
27207     addItem : function(cfg)
27208     {
27209         var item = new Roo.bootstrap.NavProgressItem(cfg);
27210         
27211         item.parentId = this.id;
27212         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27213         
27214         if(cfg.html){
27215             var top = new Roo.bootstrap.Element({
27216                 tag : 'div',
27217                 cls : 'roo-navigation-bar-text'
27218             });
27219             
27220             var bottom = new Roo.bootstrap.Element({
27221                 tag : 'div',
27222                 cls : 'roo-navigation-bar-text'
27223             });
27224             
27225             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27226             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27227             
27228             var topText = new Roo.bootstrap.Element({
27229                 tag : 'span',
27230                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27231             });
27232             
27233             var bottomText = new Roo.bootstrap.Element({
27234                 tag : 'span',
27235                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27236             });
27237             
27238             topText.onRender(top.el, null);
27239             bottomText.onRender(bottom.el, null);
27240             
27241             item.topEl = top;
27242             item.bottomEl = bottom;
27243         }
27244         
27245         this.barItems.push(item);
27246         
27247         return item;
27248     },
27249     
27250     getActive : function()
27251     {
27252         var active = false;
27253         
27254         Roo.each(this.barItems, function(v){
27255             
27256             if (!v.isActive()) {
27257                 return;
27258             }
27259             
27260             active = v;
27261             return false;
27262             
27263         });
27264         
27265         return active;
27266     },
27267     
27268     setActiveItem : function(item)
27269     {
27270         var prev = false;
27271         
27272         Roo.each(this.barItems, function(v){
27273             if (v.rid == item.rid) {
27274                 return ;
27275             }
27276             
27277             if (v.isActive()) {
27278                 v.setActive(false);
27279                 prev = v;
27280             }
27281         });
27282
27283         item.setActive(true);
27284         
27285         this.fireEvent('changed', this, item, prev);
27286     },
27287     
27288     getBarItem: function(rid)
27289     {
27290         var ret = false;
27291         
27292         Roo.each(this.barItems, function(e) {
27293             if (e.rid != rid) {
27294                 return;
27295             }
27296             
27297             ret =  e;
27298             return false;
27299         });
27300         
27301         return ret;
27302     },
27303     
27304     indexOfItem : function(item)
27305     {
27306         var index = false;
27307         
27308         Roo.each(this.barItems, function(v, i){
27309             
27310             if (v.rid != item.rid) {
27311                 return;
27312             }
27313             
27314             index = i;
27315             return false
27316         });
27317         
27318         return index;
27319     },
27320     
27321     setActiveNext : function()
27322     {
27323         var i = this.indexOfItem(this.getActive());
27324         
27325         if (i > this.barItems.length) {
27326             return;
27327         }
27328         
27329         this.setActiveItem(this.barItems[i+1]);
27330     },
27331     
27332     setActivePrev : function()
27333     {
27334         var i = this.indexOfItem(this.getActive());
27335         
27336         if (i  < 1) {
27337             return;
27338         }
27339         
27340         this.setActiveItem(this.barItems[i-1]);
27341     },
27342     
27343     format : function()
27344     {
27345         if(!this.barItems.length){
27346             return;
27347         }
27348      
27349         var width = 100 / this.barItems.length;
27350         
27351         Roo.each(this.barItems, function(i){
27352             i.el.setStyle('width', width + '%');
27353             i.topEl.el.setStyle('width', width + '%');
27354             i.bottomEl.el.setStyle('width', width + '%');
27355         }, this);
27356         
27357     }
27358     
27359 });
27360 /*
27361  * - LGPL
27362  *
27363  * Nav Progress Item
27364  * 
27365  */
27366
27367 /**
27368  * @class Roo.bootstrap.NavProgressItem
27369  * @extends Roo.bootstrap.Component
27370  * Bootstrap NavProgressItem class
27371  * @cfg {String} rid the reference id
27372  * @cfg {Boolean} active (true|false) Is item active default false
27373  * @cfg {Boolean} disabled (true|false) Is item active default false
27374  * @cfg {String} html
27375  * @cfg {String} position (top|bottom) text position default bottom
27376  * @cfg {String} icon show icon instead of number
27377  * 
27378  * @constructor
27379  * Create a new NavProgressItem
27380  * @param {Object} config The config object
27381  */
27382 Roo.bootstrap.NavProgressItem = function(config){
27383     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27384     this.addEvents({
27385         // raw events
27386         /**
27387          * @event click
27388          * The raw click event for the entire grid.
27389          * @param {Roo.bootstrap.NavProgressItem} this
27390          * @param {Roo.EventObject} e
27391          */
27392         "click" : true
27393     });
27394    
27395 };
27396
27397 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27398     
27399     rid : '',
27400     active : false,
27401     disabled : false,
27402     html : '',
27403     position : 'bottom',
27404     icon : false,
27405     
27406     getAutoCreate : function()
27407     {
27408         var iconCls = 'roo-navigation-bar-item-icon';
27409         
27410         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27411         
27412         var cfg = {
27413             tag: 'li',
27414             cls: 'roo-navigation-bar-item',
27415             cn : [
27416                 {
27417                     tag : 'i',
27418                     cls : iconCls
27419                 }
27420             ]
27421         };
27422         
27423         if(this.active){
27424             cfg.cls += ' active';
27425         }
27426         if(this.disabled){
27427             cfg.cls += ' disabled';
27428         }
27429         
27430         return cfg;
27431     },
27432     
27433     disable : function()
27434     {
27435         this.setDisabled(true);
27436     },
27437     
27438     enable : function()
27439     {
27440         this.setDisabled(false);
27441     },
27442     
27443     initEvents: function() 
27444     {
27445         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27446         
27447         this.iconEl.on('click', this.onClick, this);
27448     },
27449     
27450     onClick : function(e)
27451     {
27452         e.preventDefault();
27453         
27454         if(this.disabled){
27455             return;
27456         }
27457         
27458         if(this.fireEvent('click', this, e) === false){
27459             return;
27460         };
27461         
27462         this.parent().setActiveItem(this);
27463     },
27464     
27465     isActive: function () 
27466     {
27467         return this.active;
27468     },
27469     
27470     setActive : function(state)
27471     {
27472         if(this.active == state){
27473             return;
27474         }
27475         
27476         this.active = state;
27477         
27478         if (state) {
27479             this.el.addClass('active');
27480             return;
27481         }
27482         
27483         this.el.removeClass('active');
27484         
27485         return;
27486     },
27487     
27488     setDisabled : function(state)
27489     {
27490         if(this.disabled == state){
27491             return;
27492         }
27493         
27494         this.disabled = state;
27495         
27496         if (state) {
27497             this.el.addClass('disabled');
27498             return;
27499         }
27500         
27501         this.el.removeClass('disabled');
27502     },
27503     
27504     tooltipEl : function()
27505     {
27506         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27507     }
27508 });
27509  
27510
27511  /*
27512  * - LGPL
27513  *
27514  * FieldLabel
27515  * 
27516  */
27517
27518 /**
27519  * @class Roo.bootstrap.FieldLabel
27520  * @extends Roo.bootstrap.Component
27521  * Bootstrap FieldLabel class
27522  * @cfg {String} html contents of the element
27523  * @cfg {String} tag tag of the element default label
27524  * @cfg {String} cls class of the element
27525  * @cfg {String} target label target 
27526  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27527  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27528  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27529  * @cfg {String} iconTooltip default "This field is required"
27530  * 
27531  * @constructor
27532  * Create a new FieldLabel
27533  * @param {Object} config The config object
27534  */
27535
27536 Roo.bootstrap.FieldLabel = function(config){
27537     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27538     
27539     this.addEvents({
27540             /**
27541              * @event invalid
27542              * Fires after the field has been marked as invalid.
27543              * @param {Roo.form.FieldLabel} this
27544              * @param {String} msg The validation message
27545              */
27546             invalid : true,
27547             /**
27548              * @event valid
27549              * Fires after the field has been validated with no errors.
27550              * @param {Roo.form.FieldLabel} this
27551              */
27552             valid : true
27553         });
27554 };
27555
27556 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27557     
27558     tag: 'label',
27559     cls: '',
27560     html: '',
27561     target: '',
27562     allowBlank : true,
27563     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27564     validClass : 'text-success fa fa-lg fa-check',
27565     iconTooltip : 'This field is required',
27566     
27567     getAutoCreate : function(){
27568         
27569         var cfg = {
27570             tag : this.tag,
27571             cls : 'roo-bootstrap-field-label ' + this.cls,
27572             for : this.target,
27573             cn : [
27574                 {
27575                     tag : 'i',
27576                     cls : '',
27577                     tooltip : this.iconTooltip
27578                 },
27579                 {
27580                     tag : 'span',
27581                     html : this.html
27582                 }
27583             ] 
27584         };
27585         
27586         return cfg;
27587     },
27588     
27589     initEvents: function() 
27590     {
27591         Roo.bootstrap.Element.superclass.initEvents.call(this);
27592         
27593         this.iconEl = this.el.select('i', true).first();
27594         
27595         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27596         
27597         Roo.bootstrap.FieldLabel.register(this);
27598     },
27599     
27600     /**
27601      * Mark this field as valid
27602      */
27603     markValid : function()
27604     {
27605         this.iconEl.show();
27606         
27607         this.iconEl.removeClass(this.invalidClass);
27608         
27609         this.iconEl.addClass(this.validClass);
27610         
27611         this.fireEvent('valid', this);
27612     },
27613     
27614     /**
27615      * Mark this field as invalid
27616      * @param {String} msg The validation message
27617      */
27618     markInvalid : function(msg)
27619     {
27620         this.iconEl.show();
27621         
27622         this.iconEl.removeClass(this.validClass);
27623         
27624         this.iconEl.addClass(this.invalidClass);
27625         
27626         this.fireEvent('invalid', this, msg);
27627     }
27628     
27629    
27630 });
27631
27632 Roo.apply(Roo.bootstrap.FieldLabel, {
27633     
27634     groups: {},
27635     
27636      /**
27637     * register a FieldLabel Group
27638     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27639     */
27640     register : function(label)
27641     {
27642         if(this.groups.hasOwnProperty(label.target)){
27643             return;
27644         }
27645      
27646         this.groups[label.target] = label;
27647         
27648     },
27649     /**
27650     * fetch a FieldLabel Group based on the target
27651     * @param {string} target
27652     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27653     */
27654     get: function(target) {
27655         if (typeof(this.groups[target]) == 'undefined') {
27656             return false;
27657         }
27658         
27659         return this.groups[target] ;
27660     }
27661 });
27662
27663  
27664
27665  /*
27666  * - LGPL
27667  *
27668  * page DateSplitField.
27669  * 
27670  */
27671
27672
27673 /**
27674  * @class Roo.bootstrap.DateSplitField
27675  * @extends Roo.bootstrap.Component
27676  * Bootstrap DateSplitField class
27677  * @cfg {string} fieldLabel - the label associated
27678  * @cfg {Number} labelWidth set the width of label (0-12)
27679  * @cfg {String} labelAlign (top|left)
27680  * @cfg {Boolean} dayAllowBlank (true|false) default false
27681  * @cfg {Boolean} monthAllowBlank (true|false) default false
27682  * @cfg {Boolean} yearAllowBlank (true|false) default false
27683  * @cfg {string} dayPlaceholder 
27684  * @cfg {string} monthPlaceholder
27685  * @cfg {string} yearPlaceholder
27686  * @cfg {string} dayFormat default 'd'
27687  * @cfg {string} monthFormat default 'm'
27688  * @cfg {string} yearFormat default 'Y'
27689
27690  *     
27691  * @constructor
27692  * Create a new DateSplitField
27693  * @param {Object} config The config object
27694  */
27695
27696 Roo.bootstrap.DateSplitField = function(config){
27697     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27698     
27699     this.addEvents({
27700         // raw events
27701          /**
27702          * @event years
27703          * getting the data of years
27704          * @param {Roo.bootstrap.DateSplitField} this
27705          * @param {Object} years
27706          */
27707         "years" : true,
27708         /**
27709          * @event days
27710          * getting the data of days
27711          * @param {Roo.bootstrap.DateSplitField} this
27712          * @param {Object} days
27713          */
27714         "days" : true,
27715         /**
27716          * @event invalid
27717          * Fires after the field has been marked as invalid.
27718          * @param {Roo.form.Field} this
27719          * @param {String} msg The validation message
27720          */
27721         invalid : true,
27722        /**
27723          * @event valid
27724          * Fires after the field has been validated with no errors.
27725          * @param {Roo.form.Field} this
27726          */
27727         valid : true
27728     });
27729 };
27730
27731 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27732     
27733     fieldLabel : '',
27734     labelAlign : 'top',
27735     labelWidth : 3,
27736     dayAllowBlank : false,
27737     monthAllowBlank : false,
27738     yearAllowBlank : false,
27739     dayPlaceholder : '',
27740     monthPlaceholder : '',
27741     yearPlaceholder : '',
27742     dayFormat : 'd',
27743     monthFormat : 'm',
27744     yearFormat : 'Y',
27745     isFormField : true,
27746     
27747     getAutoCreate : function()
27748     {
27749         var cfg = {
27750             tag : 'div',
27751             cls : 'row roo-date-split-field-group',
27752             cn : [
27753                 {
27754                     tag : 'input',
27755                     type : 'hidden',
27756                     cls : 'form-hidden-field roo-date-split-field-group-value',
27757                     name : this.name
27758                 }
27759             ]
27760         };
27761         
27762         if(this.fieldLabel){
27763             cfg.cn.push({
27764                 tag : 'div',
27765                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27766                 cn : [
27767                     {
27768                         tag : 'label',
27769                         html : this.fieldLabel
27770                     }
27771                 ]
27772             });
27773         }
27774         
27775         Roo.each(['day', 'month', 'year'], function(t){
27776             cfg.cn.push({
27777                 tag : 'div',
27778                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27779             });
27780         }, this);
27781         
27782         return cfg;
27783     },
27784     
27785     inputEl: function ()
27786     {
27787         return this.el.select('.roo-date-split-field-group-value', true).first();
27788     },
27789     
27790     onRender : function(ct, position) 
27791     {
27792         var _this = this;
27793         
27794         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27795         
27796         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27797         
27798         this.dayField = new Roo.bootstrap.ComboBox({
27799             allowBlank : this.dayAllowBlank,
27800             alwaysQuery : true,
27801             displayField : 'value',
27802             editable : false,
27803             fieldLabel : '',
27804             forceSelection : true,
27805             mode : 'local',
27806             placeholder : this.dayPlaceholder,
27807             selectOnFocus : true,
27808             tpl : '<div class="select2-result"><b>{value}</b></div>',
27809             triggerAction : 'all',
27810             typeAhead : true,
27811             valueField : 'value',
27812             store : new Roo.data.SimpleStore({
27813                 data : (function() {    
27814                     var days = [];
27815                     _this.fireEvent('days', _this, days);
27816                     return days;
27817                 })(),
27818                 fields : [ 'value' ]
27819             }),
27820             listeners : {
27821                 select : function (_self, record, index)
27822                 {
27823                     _this.setValue(_this.getValue());
27824                 }
27825             }
27826         });
27827
27828         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27829         
27830         this.monthField = new Roo.bootstrap.MonthField({
27831             after : '<i class=\"fa fa-calendar\"></i>',
27832             allowBlank : this.monthAllowBlank,
27833             placeholder : this.monthPlaceholder,
27834             readOnly : true,
27835             listeners : {
27836                 render : function (_self)
27837                 {
27838                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27839                         e.preventDefault();
27840                         _self.focus();
27841                     });
27842                 },
27843                 select : function (_self, oldvalue, newvalue)
27844                 {
27845                     _this.setValue(_this.getValue());
27846                 }
27847             }
27848         });
27849         
27850         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27851         
27852         this.yearField = new Roo.bootstrap.ComboBox({
27853             allowBlank : this.yearAllowBlank,
27854             alwaysQuery : true,
27855             displayField : 'value',
27856             editable : false,
27857             fieldLabel : '',
27858             forceSelection : true,
27859             mode : 'local',
27860             placeholder : this.yearPlaceholder,
27861             selectOnFocus : true,
27862             tpl : '<div class="select2-result"><b>{value}</b></div>',
27863             triggerAction : 'all',
27864             typeAhead : true,
27865             valueField : 'value',
27866             store : new Roo.data.SimpleStore({
27867                 data : (function() {
27868                     var years = [];
27869                     _this.fireEvent('years', _this, years);
27870                     return years;
27871                 })(),
27872                 fields : [ 'value' ]
27873             }),
27874             listeners : {
27875                 select : function (_self, record, index)
27876                 {
27877                     _this.setValue(_this.getValue());
27878                 }
27879             }
27880         });
27881
27882         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27883     },
27884     
27885     setValue : function(v, format)
27886     {
27887         this.inputEl.dom.value = v;
27888         
27889         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27890         
27891         var d = Date.parseDate(v, f);
27892         
27893         if(!d){
27894             this.validate();
27895             return;
27896         }
27897         
27898         this.setDay(d.format(this.dayFormat));
27899         this.setMonth(d.format(this.monthFormat));
27900         this.setYear(d.format(this.yearFormat));
27901         
27902         this.validate();
27903         
27904         return;
27905     },
27906     
27907     setDay : function(v)
27908     {
27909         this.dayField.setValue(v);
27910         this.inputEl.dom.value = this.getValue();
27911         this.validate();
27912         return;
27913     },
27914     
27915     setMonth : function(v)
27916     {
27917         this.monthField.setValue(v, true);
27918         this.inputEl.dom.value = this.getValue();
27919         this.validate();
27920         return;
27921     },
27922     
27923     setYear : function(v)
27924     {
27925         this.yearField.setValue(v);
27926         this.inputEl.dom.value = this.getValue();
27927         this.validate();
27928         return;
27929     },
27930     
27931     getDay : function()
27932     {
27933         return this.dayField.getValue();
27934     },
27935     
27936     getMonth : function()
27937     {
27938         return this.monthField.getValue();
27939     },
27940     
27941     getYear : function()
27942     {
27943         return this.yearField.getValue();
27944     },
27945     
27946     getValue : function()
27947     {
27948         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27949         
27950         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27951         
27952         return date;
27953     },
27954     
27955     reset : function()
27956     {
27957         this.setDay('');
27958         this.setMonth('');
27959         this.setYear('');
27960         this.inputEl.dom.value = '';
27961         this.validate();
27962         return;
27963     },
27964     
27965     validate : function()
27966     {
27967         var d = this.dayField.validate();
27968         var m = this.monthField.validate();
27969         var y = this.yearField.validate();
27970         
27971         var valid = true;
27972         
27973         if(
27974                 (!this.dayAllowBlank && !d) ||
27975                 (!this.monthAllowBlank && !m) ||
27976                 (!this.yearAllowBlank && !y)
27977         ){
27978             valid = false;
27979         }
27980         
27981         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
27982             return valid;
27983         }
27984         
27985         if(valid){
27986             this.markValid();
27987             return valid;
27988         }
27989         
27990         this.markInvalid();
27991         
27992         return valid;
27993     },
27994     
27995     markValid : function()
27996     {
27997         
27998         var label = this.el.select('label', true).first();
27999         var icon = this.el.select('i.fa-star', true).first();
28000
28001         if(label && icon){
28002             icon.remove();
28003         }
28004         
28005         this.fireEvent('valid', this);
28006     },
28007     
28008      /**
28009      * Mark this field as invalid
28010      * @param {String} msg The validation message
28011      */
28012     markInvalid : function(msg)
28013     {
28014         
28015         var label = this.el.select('label', true).first();
28016         var icon = this.el.select('i.fa-star', true).first();
28017
28018         if(label && !icon){
28019             this.el.select('.roo-date-split-field-label', true).createChild({
28020                 tag : 'i',
28021                 cls : 'text-danger fa fa-lg fa-star',
28022                 tooltip : 'This field is required',
28023                 style : 'margin-right:5px;'
28024             }, label, true);
28025         }
28026         
28027         this.fireEvent('invalid', this, msg);
28028     },
28029     
28030     clearInvalid : function()
28031     {
28032         var label = this.el.select('label', true).first();
28033         var icon = this.el.select('i.fa-star', true).first();
28034
28035         if(label && icon){
28036             icon.remove();
28037         }
28038         
28039         this.fireEvent('valid', this);
28040     },
28041     
28042     getName: function()
28043     {
28044         return this.name;
28045     }
28046     
28047 });
28048
28049