docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         }
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             }
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 }
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         }
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             }
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         }
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1969
1970         this.el.on("mouseover", this.onMouseOver, this);
1971         this.el.on("mouseout", this.onMouseOut, this);
1972         
1973         
1974     },
1975     findTargetItem : function(e){
1976         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1977         if(!t){
1978             return false;
1979         }
1980         //Roo.log(t);         Roo.log(t.id);
1981         if(t && t.id){
1982             //Roo.log(this.menuitems);
1983             return this.menuitems.get(t.id);
1984             
1985             //return this.items.get(t.menuItemId);
1986         }
1987         
1988         return false;
1989     },
1990     onClick : function(e){
1991         Roo.log("menu.onClick");
1992         var t = this.findTargetItem(e);
1993         if(!t || t.isContainer){
1994             return;
1995         }
1996         Roo.log(e);
1997         /*
1998         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1999             if(t == this.activeItem && t.shouldDeactivate(e)){
2000                 this.activeItem.deactivate();
2001                 delete this.activeItem;
2002                 return;
2003             }
2004             if(t.canActivate){
2005                 this.setActiveItem(t, true);
2006             }
2007             return;
2008             
2009             
2010         }
2011         */
2012        
2013         Roo.log('pass click event');
2014         
2015         t.onClick(e);
2016         
2017         this.fireEvent("click", this, t, e);
2018         
2019         this.hide();
2020     },
2021      onMouseOver : function(e){
2022         var t  = this.findTargetItem(e);
2023         //Roo.log(t);
2024         //if(t){
2025         //    if(t.canActivate && !t.disabled){
2026         //        this.setActiveItem(t, true);
2027         //    }
2028         //}
2029         
2030         this.fireEvent("mouseover", this, e, t);
2031     },
2032     isVisible : function(){
2033         return !this.hidden;
2034     },
2035      onMouseOut : function(e){
2036         var t  = this.findTargetItem(e);
2037         
2038         //if(t ){
2039         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2040         //        this.activeItem.deactivate();
2041         //        delete this.activeItem;
2042         //    }
2043         //}
2044         this.fireEvent("mouseout", this, e, t);
2045     },
2046     
2047     
2048     /**
2049      * Displays this menu relative to another element
2050      * @param {String/HTMLElement/Roo.Element} element The element to align to
2051      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052      * the element (defaults to this.defaultAlign)
2053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2054      */
2055     show : function(el, pos, parentMenu){
2056         this.parentMenu = parentMenu;
2057         if(!this.el){
2058             this.render();
2059         }
2060         this.fireEvent("beforeshow", this);
2061         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2062     },
2063      /**
2064      * Displays this menu at a specific xy position
2065      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     showAt : function(xy, parentMenu, /* private: */_e){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         if(_e !== false){
2074             this.fireEvent("beforeshow", this);
2075             //xy = this.el.adjustForConstraints(xy);
2076         }
2077         
2078         //this.el.show();
2079         this.hideMenuItems();
2080         this.hidden = false;
2081         this.triggerEl.addClass('open');
2082         
2083         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2085         }
2086         
2087         this.el.setXY(xy);
2088         this.focus();
2089         this.fireEvent("show", this);
2090     },
2091     
2092     focus : function(){
2093         return;
2094         if(!this.hidden){
2095             this.doFocus.defer(50, this);
2096         }
2097     },
2098
2099     doFocus : function(){
2100         if(!this.hidden){
2101             this.focusEl.focus();
2102         }
2103     },
2104
2105     /**
2106      * Hides this menu and optionally all parent menus
2107      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2108      */
2109     hide : function(deep){
2110         
2111         this.hideMenuItems();
2112         if(this.el && this.isVisible()){
2113             this.fireEvent("beforehide", this);
2114             if(this.activeItem){
2115                 this.activeItem.deactivate();
2116                 this.activeItem = null;
2117             }
2118             this.triggerEl.removeClass('open');;
2119             this.hidden = true;
2120             this.fireEvent("hide", this);
2121         }
2122         if(deep === true && this.parentMenu){
2123             this.parentMenu.hide(true);
2124         }
2125     },
2126     
2127     onTriggerPress  : function(e)
2128     {
2129         
2130         Roo.log('trigger press');
2131         //Roo.log(e.getTarget());
2132        // Roo.log(this.triggerEl.dom);
2133         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2134             return;
2135         }
2136         
2137         if (this.isVisible()) {
2138             Roo.log('hide');
2139             this.hide();
2140         } else {
2141             Roo.log('show');
2142             this.show(this.triggerEl, false, false);
2143         }
2144         
2145         e.stopEvent();
2146     },
2147     
2148          
2149        
2150     
2151     hideMenuItems : function()
2152     {
2153         //$(backdrop).remove()
2154         Roo.select('.open',true).each(function(aa) {
2155             
2156             aa.removeClass('open');
2157           //var parent = getParent($(this))
2158           //var relatedTarget = { relatedTarget: this }
2159           
2160            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161           //if (e.isDefaultPrevented()) return
2162            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2163         })
2164     },
2165     addxtypeChild : function (tree, cntr) {
2166         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2167           
2168         this.menuitems.add(comp);
2169         return comp;
2170
2171     },
2172     getEl : function()
2173     {
2174         Roo.log(this.el);
2175         return this.el;
2176     }
2177 });
2178
2179  
2180  /*
2181  * - LGPL
2182  *
2183  * menu item
2184  * 
2185  */
2186
2187
2188 /**
2189  * @class Roo.bootstrap.MenuItem
2190  * @extends Roo.bootstrap.Component
2191  * Bootstrap MenuItem class
2192  * @cfg {String} html the menu label
2193  * @cfg {String} href the link
2194  * @cfg {Boolean} preventDefault (true | false) default true
2195  * @cfg {Boolean} isContainer (true | false) default false
2196  * 
2197  * 
2198  * @constructor
2199  * Create a new MenuItem
2200  * @param {Object} config The config object
2201  */
2202
2203
2204 Roo.bootstrap.MenuItem = function(config){
2205     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2206     this.addEvents({
2207         // raw events
2208         /**
2209          * @event click
2210          * The raw click event for the entire grid.
2211          * @param {Roo.bootstrap.MenuItem} this
2212          * @param {Roo.EventObject} e
2213          */
2214         "click" : true
2215     });
2216 };
2217
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2219     
2220     href : false,
2221     html : false,
2222     preventDefault: true,
2223     isContainer : false,
2224     
2225     getAutoCreate : function(){
2226         
2227         if(this.isContainer){
2228             return {
2229                 tag: 'li',
2230                 cls: 'dropdown-menu-item'
2231             };
2232         }
2233         
2234         var cfg= {
2235             tag: 'li',
2236             cls: 'dropdown-menu-item',
2237             cn: [
2238                     {
2239                         tag : 'a',
2240                         href : '#',
2241                         html : 'Link'
2242                     }
2243                 ]
2244         };
2245         if (this.parent().type == 'treeview') {
2246             cfg.cls = 'treeview-menu';
2247         }
2248         
2249         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2251         return cfg;
2252     },
2253     
2254     initEvents: function() {
2255         
2256         //this.el.select('a').on('click', this.onClick, this);
2257         
2258     },
2259     onClick : function(e)
2260     {
2261         Roo.log('item on click ');
2262         //if(this.preventDefault){
2263         //    e.preventDefault();
2264         //}
2265         //this.parent().hideMenuItems();
2266         
2267         this.fireEvent('click', this, e);
2268     },
2269     getEl : function()
2270     {
2271         return this.el;
2272     }
2273 });
2274
2275  
2276
2277  /*
2278  * - LGPL
2279  *
2280  * menu separator
2281  * 
2282  */
2283
2284
2285 /**
2286  * @class Roo.bootstrap.MenuSeparator
2287  * @extends Roo.bootstrap.Component
2288  * Bootstrap MenuSeparator class
2289  * 
2290  * @constructor
2291  * Create a new MenuItem
2292  * @param {Object} config The config object
2293  */
2294
2295
2296 Roo.bootstrap.MenuSeparator = function(config){
2297     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2298 };
2299
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2301     
2302     getAutoCreate : function(){
2303         var cfg = {
2304             cls: 'divider',
2305             tag : 'li'
2306         };
2307         
2308         return cfg;
2309     }
2310    
2311 });
2312
2313  
2314
2315  
2316 /*
2317 * Licence: LGPL
2318 */
2319
2320 /**
2321  * @class Roo.bootstrap.Modal
2322  * @extends Roo.bootstrap.Component
2323  * Bootstrap Modal class
2324  * @cfg {String} title Title of dialog
2325  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2327  * @cfg {Boolean} specificTitle default false
2328  * @cfg {Array} buttons Array of buttons or standard button set..
2329  * @cfg {String} buttonPosition (left|right|center) default right
2330  * @cfg {Boolean} animate default true
2331  * @cfg {Boolean} allow_close default true
2332  * 
2333  * @constructor
2334  * Create a new Modal Dialog
2335  * @param {Object} config The config object
2336  */
2337
2338 Roo.bootstrap.Modal = function(config){
2339     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2340     this.addEvents({
2341         // raw events
2342         /**
2343          * @event btnclick
2344          * The raw btnclick event for the button
2345          * @param {Roo.EventObject} e
2346          */
2347         "btnclick" : true
2348     });
2349     this.buttons = this.buttons || [];
2350      
2351     if (this.tmpl) {
2352         this.tmpl = Roo.factory(this.tmpl);
2353     }
2354     
2355 };
2356
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2358     
2359     title : 'test dialog',
2360    
2361     buttons : false,
2362     
2363     // set on load...
2364      
2365     html: false,
2366     
2367     tmp: false,
2368     
2369     specificTitle: false,
2370     
2371     buttonPosition: 'right',
2372     
2373     allow_close : true,
2374     
2375     animate : true,
2376     
2377     
2378      // private
2379     bodyEl:  false,
2380     footerEl:  false,
2381     titleEl:  false,
2382     closeEl:  false,
2383     
2384     
2385     onRender : function(ct, position)
2386     {
2387         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2388      
2389         if(!this.el){
2390             var cfg = Roo.apply({},  this.getAutoCreate());
2391             cfg.id = Roo.id();
2392             //if(!cfg.name){
2393             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2394             //}
2395             //if (!cfg.name.length) {
2396             //    delete cfg.name;
2397            // }
2398             if (this.cls) {
2399                 cfg.cls += ' ' + this.cls;
2400             }
2401             if (this.style) {
2402                 cfg.style = this.style;
2403             }
2404             this.el = Roo.get(document.body).createChild(cfg, position);
2405         }
2406         //var type = this.el.dom.type;
2407         
2408         
2409         
2410         
2411         if(this.tabIndex !== undefined){
2412             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2413         }
2414         
2415         
2416         this.bodyEl = this.el.select('.modal-body',true).first();
2417         this.closeEl = this.el.select('.modal-header .close', true).first();
2418         this.footerEl = this.el.select('.modal-footer',true).first();
2419         this.titleEl = this.el.select('.modal-title',true).first();
2420         
2421         
2422          
2423         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424         this.maskEl.enableDisplayMode("block");
2425         this.maskEl.hide();
2426         //this.el.addClass("x-dlg-modal");
2427     
2428         if (this.buttons.length) {
2429             Roo.each(this.buttons, function(bb) {
2430                 var b = Roo.apply({}, bb);
2431                 b.xns = b.xns || Roo.bootstrap;
2432                 b.xtype = b.xtype || 'Button';
2433                 if (typeof(b.listeners) == 'undefined') {
2434                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2435                 }
2436                 
2437                 var btn = Roo.factory(b);
2438                 
2439                 btn.onRender(this.el.select('.modal-footer div').first());
2440                 
2441             },this);
2442         }
2443         // render the children.
2444         var nitems = [];
2445         
2446         if(typeof(this.items) != 'undefined'){
2447             var items = this.items;
2448             delete this.items;
2449
2450             for(var i =0;i < items.length;i++) {
2451                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2452             }
2453         }
2454         
2455         this.items = nitems;
2456         
2457         // where are these used - they used to be body/close/footer
2458         
2459        
2460         this.initEvents();
2461         //this.el.addClass([this.fieldClass, this.cls]);
2462         
2463     },
2464     
2465     getAutoCreate : function(){
2466         
2467         
2468         var bdy = {
2469                 cls : 'modal-body',
2470                 html : this.html || ''
2471         };
2472         
2473         var title = {
2474             tag: 'h4',
2475             cls : 'modal-title',
2476             html : this.title
2477         };
2478         
2479         if(this.specificTitle){
2480             title = this.title;
2481             
2482         };
2483         
2484         var header = [];
2485         if (this.allow_close) {
2486             header.push({
2487                 tag: 'button',
2488                 cls : 'close',
2489                 html : '&times'
2490             });
2491         }
2492         header.push(title);
2493         
2494         var modal = {
2495             cls: "modal",
2496             style : 'display: none',
2497             cn : [
2498                 {
2499                     cls: "modal-dialog",
2500                     cn : [
2501                         {
2502                             cls : "modal-content",
2503                             cn : [
2504                                 {
2505                                     cls : 'modal-header',
2506                                     cn : header
2507                                 },
2508                                 bdy,
2509                                 {
2510                                     cls : 'modal-footer',
2511                                     cn : [
2512                                         {
2513                                             tag: 'div',
2514                                             cls: 'btn-' + this.buttonPosition
2515                                         }
2516                                     ]
2517                                     
2518                                 }
2519                                 
2520                                 
2521                             ]
2522                             
2523                         }
2524                     ]
2525                         
2526                 }
2527             ]
2528         };
2529         
2530         if(this.animate){
2531             modal.cls += ' fade';
2532         }
2533         
2534         return modal;
2535           
2536     },
2537     getChildContainer : function() {
2538          
2539          return this.bodyEl;
2540         
2541     },
2542     getButtonContainer : function() {
2543          return this.el.select('.modal-footer div',true).first();
2544         
2545     },
2546     initEvents : function()
2547     {
2548         if (this.allow_close) {
2549             this.closeEl.on('click', this.hide, this);
2550         }
2551         
2552         var _this = this;
2553         
2554         window.addEventListener("resize", function() { _this.resize(); } );
2555
2556     },
2557     
2558     resize : function()
2559     {
2560         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2561     },
2562     
2563     show : function() {
2564         
2565         if (!this.rendered) {
2566             this.render();
2567         }
2568         
2569         this.el.setStyle('display', 'block');
2570         
2571         if(this.animate){
2572             var _this = this;
2573             (function(){ _this.el.addClass('in'); }).defer(50);
2574         }else{
2575             this.el.addClass('in');
2576         }
2577         
2578         // not sure how we can show data in here.. 
2579         //if (this.tmpl) {
2580         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2581         //}
2582         
2583         Roo.get(document.body).addClass("x-body-masked");
2584         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2585         this.maskEl.show();
2586         this.el.setStyle('zIndex', '10001');
2587        
2588         this.fireEvent('show', this);
2589         
2590         
2591     },
2592     hide : function()
2593     {
2594         this.maskEl.hide();
2595         Roo.get(document.body).removeClass("x-body-masked");
2596         this.el.removeClass('in');
2597         
2598         if(this.animate){
2599             var _this = this;
2600             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2601         }else{
2602             this.el.setStyle('display', 'none');
2603         }
2604         
2605         this.fireEvent('hide', this);
2606     },
2607     
2608     addButton : function(str, cb)
2609     {
2610          
2611         
2612         var b = Roo.apply({}, { html : str } );
2613         b.xns = b.xns || Roo.bootstrap;
2614         b.xtype = b.xtype || 'Button';
2615         if (typeof(b.listeners) == 'undefined') {
2616             b.listeners = { click : cb.createDelegate(this)  };
2617         }
2618         
2619         var btn = Roo.factory(b);
2620            
2621         btn.onRender(this.el.select('.modal-footer div').first());
2622         
2623         return btn;   
2624        
2625     },
2626     
2627     setDefaultButton : function(btn)
2628     {
2629         //this.el.select('.modal-footer').()
2630     },
2631     resizeTo: function(w,h)
2632     {
2633         // skip..
2634     },
2635     setContentSize  : function(w, h)
2636     {
2637         
2638     },
2639     onButtonClick: function(btn,e)
2640     {
2641         //Roo.log([a,b,c]);
2642         this.fireEvent('btnclick', btn.name, e);
2643     },
2644      /**
2645      * Set the title of the Dialog
2646      * @param {String} str new Title
2647      */
2648     setTitle: function(str) {
2649         this.titleEl.dom.innerHTML = str;    
2650     },
2651     /**
2652      * Set the body of the Dialog
2653      * @param {String} str new Title
2654      */
2655     setBody: function(str) {
2656         this.bodyEl.dom.innerHTML = str;    
2657     },
2658     /**
2659      * Set the body of the Dialog using the template
2660      * @param {Obj} data - apply this data to the template and replace the body contents.
2661      */
2662     applyBody: function(obj)
2663     {
2664         if (!this.tmpl) {
2665             Roo.log("Error - using apply Body without a template");
2666             //code
2667         }
2668         this.tmpl.overwrite(this.bodyEl, obj);
2669     }
2670     
2671 });
2672
2673
2674 Roo.apply(Roo.bootstrap.Modal,  {
2675     /**
2676          * Button config that displays a single OK button
2677          * @type Object
2678          */
2679         OK :  [{
2680             name : 'ok',
2681             weight : 'primary',
2682             html : 'OK'
2683         }], 
2684         /**
2685          * Button config that displays Yes and No buttons
2686          * @type Object
2687          */
2688         YESNO : [
2689             {
2690                 name  : 'no',
2691                 html : 'No'
2692             },
2693             {
2694                 name  :'yes',
2695                 weight : 'primary',
2696                 html : 'Yes'
2697             }
2698         ],
2699         
2700         /**
2701          * Button config that displays OK and Cancel buttons
2702          * @type Object
2703          */
2704         OKCANCEL : [
2705             {
2706                name : 'cancel',
2707                 html : 'Cancel'
2708             },
2709             {
2710                 name : 'ok',
2711                 weight : 'primary',
2712                 html : 'OK'
2713             }
2714         ],
2715         /**
2716          * Button config that displays Yes, No and Cancel buttons
2717          * @type Object
2718          */
2719         YESNOCANCEL : [
2720             {
2721                 name : 'yes',
2722                 weight : 'primary',
2723                 html : 'Yes'
2724             },
2725             {
2726                 name : 'no',
2727                 html : 'No'
2728             },
2729             {
2730                 name : 'cancel',
2731                 html : 'Cancel'
2732             }
2733         ]
2734 });
2735  
2736  /*
2737  * - LGPL
2738  *
2739  * messagebox - can be used as a replace
2740  * 
2741  */
2742 /**
2743  * @class Roo.MessageBox
2744  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2745  * Example usage:
2746  *<pre><code>
2747 // Basic alert:
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2749
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2752     if (btn == 'ok'){
2753         // process text value...
2754     }
2755 });
2756
2757 // Show a dialog using config options:
2758 Roo.Msg.show({
2759    title:'Save Changes?',
2760    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761    buttons: Roo.Msg.YESNOCANCEL,
2762    fn: processResult,
2763    animEl: 'elId'
2764 });
2765 </code></pre>
2766  * @singleton
2767  */
2768 Roo.bootstrap.MessageBox = function(){
2769     var dlg, opt, mask, waitTimer;
2770     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771     var buttons, activeTextEl, bwidth;
2772
2773     
2774     // private
2775     var handleButton = function(button){
2776         dlg.hide();
2777         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2778     };
2779
2780     // private
2781     var handleHide = function(){
2782         if(opt && opt.cls){
2783             dlg.el.removeClass(opt.cls);
2784         }
2785         //if(waitTimer){
2786         //    Roo.TaskMgr.stop(waitTimer);
2787         //    waitTimer = null;
2788         //}
2789     };
2790
2791     // private
2792     var updateButtons = function(b){
2793         var width = 0;
2794         if(!b){
2795             buttons["ok"].hide();
2796             buttons["cancel"].hide();
2797             buttons["yes"].hide();
2798             buttons["no"].hide();
2799             //dlg.footer.dom.style.display = 'none';
2800             return width;
2801         }
2802         dlg.footerEl.dom.style.display = '';
2803         for(var k in buttons){
2804             if(typeof buttons[k] != "function"){
2805                 if(b[k]){
2806                     buttons[k].show();
2807                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808                     width += buttons[k].el.getWidth()+15;
2809                 }else{
2810                     buttons[k].hide();
2811                 }
2812             }
2813         }
2814         return width;
2815     };
2816
2817     // private
2818     var handleEsc = function(d, k, e){
2819         if(opt && opt.closable !== false){
2820             dlg.hide();
2821         }
2822         if(e){
2823             e.stopEvent();
2824         }
2825     };
2826
2827     return {
2828         /**
2829          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830          * @return {Roo.BasicDialog} The BasicDialog element
2831          */
2832         getDialog : function(){
2833            if(!dlg){
2834                 dlg = new Roo.bootstrap.Modal( {
2835                     //draggable: true,
2836                     //resizable:false,
2837                     //constraintoviewport:false,
2838                     //fixedcenter:true,
2839                     //collapsible : false,
2840                     //shim:true,
2841                     //modal: true,
2842                   //  width:400,
2843                   //  height:100,
2844                     //buttonAlign:"center",
2845                     closeClick : function(){
2846                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2847                             handleButton("no");
2848                         }else{
2849                             handleButton("cancel");
2850                         }
2851                     }
2852                 });
2853                 dlg.render();
2854                 dlg.on("hide", handleHide);
2855                 mask = dlg.mask;
2856                 //dlg.addKeyListener(27, handleEsc);
2857                 buttons = {};
2858                 this.buttons = buttons;
2859                 var bt = this.buttonText;
2860                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2864                 Roo.log(buttons)
2865                 bodyEl = dlg.bodyEl.createChild({
2866
2867                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868                         '<textarea class="roo-mb-textarea"></textarea>' +
2869                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2870                 });
2871                 msgEl = bodyEl.dom.firstChild;
2872                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873                 textboxEl.enableDisplayMode();
2874                 textboxEl.addKeyListener([10,13], function(){
2875                     if(dlg.isVisible() && opt && opt.buttons){
2876                         if(opt.buttons.ok){
2877                             handleButton("ok");
2878                         }else if(opt.buttons.yes){
2879                             handleButton("yes");
2880                         }
2881                     }
2882                 });
2883                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884                 textareaEl.enableDisplayMode();
2885                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886                 progressEl.enableDisplayMode();
2887                 var pf = progressEl.dom.firstChild;
2888                 if (pf) {
2889                     pp = Roo.get(pf.firstChild);
2890                     pp.setHeight(pf.offsetHeight);
2891                 }
2892                 
2893             }
2894             return dlg;
2895         },
2896
2897         /**
2898          * Updates the message box body text
2899          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900          * the XHTML-compliant non-breaking space character '&amp;#160;')
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         updateText : function(text){
2904             if(!dlg.isVisible() && !opt.width){
2905                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2906             }
2907             msgEl.innerHTML = text || '&#160;';
2908       
2909             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2911             var w = Math.max(
2912                     Math.min(opt.width || cw , this.maxWidth), 
2913                     Math.max(opt.minWidth || this.minWidth, bwidth)
2914             );
2915             if(opt.prompt){
2916                 activeTextEl.setWidth(w);
2917             }
2918             if(dlg.isVisible()){
2919                 dlg.fixedcenter = false;
2920             }
2921             // to big, make it scroll. = But as usual stupid IE does not support
2922             // !important..
2923             
2924             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2927             } else {
2928                 bodyEl.dom.style.height = '';
2929                 bodyEl.dom.style.overflowY = '';
2930             }
2931             if (cw > w) {
2932                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2933             } else {
2934                 bodyEl.dom.style.overflowX = '';
2935             }
2936             
2937             dlg.setContentSize(w, bodyEl.getHeight());
2938             if(dlg.isVisible()){
2939                 dlg.fixedcenter = true;
2940             }
2941             return this;
2942         },
2943
2944         /**
2945          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2946          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949          * @return {Roo.MessageBox} This message box
2950          */
2951         updateProgress : function(value, text){
2952             if(text){
2953                 this.updateText(text);
2954             }
2955             if (pp) { // weird bug on my firefox - for some reason this is not defined
2956                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2957             }
2958             return this;
2959         },        
2960
2961         /**
2962          * Returns true if the message box is currently displayed
2963          * @return {Boolean} True if the message box is visible, else false
2964          */
2965         isVisible : function(){
2966             return dlg && dlg.isVisible();  
2967         },
2968
2969         /**
2970          * Hides the message box if it is displayed
2971          */
2972         hide : function(){
2973             if(this.isVisible()){
2974                 dlg.hide();
2975             }  
2976         },
2977
2978         /**
2979          * Displays a new message box, or reinitializes an existing message box, based on the config options
2980          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981          * The following config object properties are supported:
2982          * <pre>
2983 Property    Type             Description
2984 ----------  ---------------  ------------------------------------------------------------------------------------
2985 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2986                                    closes (defaults to undefined)
2987 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2990                                    progress and wait dialogs will ignore this property and always hide the
2991                                    close button as they can only be closed programmatically.
2992 cls               String           A custom CSS class to apply to the message box element
2993 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2994                                    displayed (defaults to 75)
2995 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2996                                    function will be btn (the name of the button that was clicked, if applicable,
2997                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2998                                    Progress and wait dialogs will ignore this option since they do not respond to
2999                                    user actions and can only be closed programmatically, so any required function
3000                                    should be called by the same code after it closes the dialog.
3001 icon              String           A CSS class that provides a background image to be used as an icon for
3002                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3004 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3005 modal             Boolean          False to allow user interaction with the page while the message box is
3006                                    displayed (defaults to true)
3007 msg               String           A string that will replace the existing message box body text (defaults
3008                                    to the XHTML-compliant non-breaking space character '&#160;')
3009 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3010 progress          Boolean          True to display a progress bar (defaults to false)
3011 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3014 title             String           The title text
3015 value             String           The string value to set into the active textbox element if displayed
3016 wait              Boolean          True to display a progress bar (defaults to false)
3017 width             Number           The width of the dialog in pixels
3018 </pre>
3019          *
3020          * Example usage:
3021          * <pre><code>
3022 Roo.Msg.show({
3023    title: 'Address',
3024    msg: 'Please enter your address:',
3025    width: 300,
3026    buttons: Roo.MessageBox.OKCANCEL,
3027    multiline: true,
3028    fn: saveAddress,
3029    animEl: 'addAddressBtn'
3030 });
3031 </code></pre>
3032          * @param {Object} config Configuration options
3033          * @return {Roo.MessageBox} This message box
3034          */
3035         show : function(options)
3036         {
3037             
3038             // this causes nightmares if you show one dialog after another
3039             // especially on callbacks..
3040              
3041             if(this.isVisible()){
3042                 
3043                 this.hide();
3044                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3046                 Roo.log("New Dialog Message:" +  options.msg )
3047                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3049                 
3050             }
3051             var d = this.getDialog();
3052             opt = options;
3053             d.setTitle(opt.title || "&#160;");
3054             d.closeEl.setDisplayed(opt.closable !== false);
3055             activeTextEl = textboxEl;
3056             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3057             if(opt.prompt){
3058                 if(opt.multiline){
3059                     textboxEl.hide();
3060                     textareaEl.show();
3061                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3062                         opt.multiline : this.defaultTextHeight);
3063                     activeTextEl = textareaEl;
3064                 }else{
3065                     textboxEl.show();
3066                     textareaEl.hide();
3067                 }
3068             }else{
3069                 textboxEl.hide();
3070                 textareaEl.hide();
3071             }
3072             progressEl.setDisplayed(opt.progress === true);
3073             this.updateProgress(0);
3074             activeTextEl.dom.value = opt.value || "";
3075             if(opt.prompt){
3076                 dlg.setDefaultButton(activeTextEl);
3077             }else{
3078                 var bs = opt.buttons;
3079                 var db = null;
3080                 if(bs && bs.ok){
3081                     db = buttons["ok"];
3082                 }else if(bs && bs.yes){
3083                     db = buttons["yes"];
3084                 }
3085                 dlg.setDefaultButton(db);
3086             }
3087             bwidth = updateButtons(opt.buttons);
3088             this.updateText(opt.msg);
3089             if(opt.cls){
3090                 d.el.addClass(opt.cls);
3091             }
3092             d.proxyDrag = opt.proxyDrag === true;
3093             d.modal = opt.modal !== false;
3094             d.mask = opt.modal !== false ? mask : false;
3095             if(!d.isVisible()){
3096                 // force it to the end of the z-index stack so it gets a cursor in FF
3097                 document.body.appendChild(dlg.el.dom);
3098                 d.animateTarget = null;
3099                 d.show(options.animEl);
3100             }
3101             return this;
3102         },
3103
3104         /**
3105          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3106          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107          * and closing the message box when the process is complete.
3108          * @param {String} title The title bar text
3109          * @param {String} msg The message box body text
3110          * @return {Roo.MessageBox} This message box
3111          */
3112         progress : function(title, msg){
3113             this.show({
3114                 title : title,
3115                 msg : msg,
3116                 buttons: false,
3117                 progress:true,
3118                 closable:false,
3119                 minWidth: this.minProgressWidth,
3120                 modal : true
3121             });
3122             return this;
3123         },
3124
3125         /**
3126          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127          * If a callback function is passed it will be called after the user clicks the button, and the
3128          * id of the button that was clicked will be passed as the only parameter to the callback
3129          * (could also be the top-right close button).
3130          * @param {String} title The title bar text
3131          * @param {String} msg The message box body text
3132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133          * @param {Object} scope (optional) The scope of the callback function
3134          * @return {Roo.MessageBox} This message box
3135          */
3136         alert : function(title, msg, fn, scope){
3137             this.show({
3138                 title : title,
3139                 msg : msg,
3140                 buttons: this.OK,
3141                 fn: fn,
3142                 scope : scope,
3143                 modal : true
3144             });
3145             return this;
3146         },
3147
3148         /**
3149          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3150          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151          * You are responsible for closing the message box when the process is complete.
3152          * @param {String} msg The message box body text
3153          * @param {String} title (optional) The title bar text
3154          * @return {Roo.MessageBox} This message box
3155          */
3156         wait : function(msg, title){
3157             this.show({
3158                 title : title,
3159                 msg : msg,
3160                 buttons: false,
3161                 closable:false,
3162                 progress:true,
3163                 modal:true,
3164                 width:300,
3165                 wait:true
3166             });
3167             waitTimer = Roo.TaskMgr.start({
3168                 run: function(i){
3169                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3170                 },
3171                 interval: 1000
3172             });
3173             return this;
3174         },
3175
3176         /**
3177          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180          * @param {String} title The title bar text
3181          * @param {String} msg The message box body text
3182          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183          * @param {Object} scope (optional) The scope of the callback function
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         confirm : function(title, msg, fn, scope){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: this.YESNO,
3191                 fn: fn,
3192                 scope : scope,
3193                 modal : true
3194             });
3195             return this;
3196         },
3197
3198         /**
3199          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3201          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202          * (could also be the top-right close button) and the text that was entered will be passed as the two
3203          * parameters to the callback.
3204          * @param {String} title The title bar text
3205          * @param {String} msg The message box body text
3206          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207          * @param {Object} scope (optional) The scope of the callback function
3208          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210          * @return {Roo.MessageBox} This message box
3211          */
3212         prompt : function(title, msg, fn, scope, multiline){
3213             this.show({
3214                 title : title,
3215                 msg : msg,
3216                 buttons: this.OKCANCEL,
3217                 fn: fn,
3218                 minWidth:250,
3219                 scope : scope,
3220                 prompt:true,
3221                 multiline: multiline,
3222                 modal : true
3223             });
3224             return this;
3225         },
3226
3227         /**
3228          * Button config that displays a single OK button
3229          * @type Object
3230          */
3231         OK : {ok:true},
3232         /**
3233          * Button config that displays Yes and No buttons
3234          * @type Object
3235          */
3236         YESNO : {yes:true, no:true},
3237         /**
3238          * Button config that displays OK and Cancel buttons
3239          * @type Object
3240          */
3241         OKCANCEL : {ok:true, cancel:true},
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : {yes:true, no:true, cancel:true},
3247
3248         /**
3249          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3250          * @type Number
3251          */
3252         defaultTextHeight : 75,
3253         /**
3254          * The maximum width in pixels of the message box (defaults to 600)
3255          * @type Number
3256          */
3257         maxWidth : 600,
3258         /**
3259          * The minimum width in pixels of the message box (defaults to 100)
3260          * @type Number
3261          */
3262         minWidth : 100,
3263         /**
3264          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3265          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3266          * @type Number
3267          */
3268         minProgressWidth : 250,
3269         /**
3270          * An object containing the default button text strings that can be overriden for localized language support.
3271          * Supported properties are: ok, cancel, yes and no.
3272          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3273          * @type Object
3274          */
3275         buttonText : {
3276             ok : "OK",
3277             cancel : "Cancel",
3278             yes : "Yes",
3279             no : "No"
3280         }
3281     };
3282 }();
3283
3284 /**
3285  * Shorthand for {@link Roo.MessageBox}
3286  */
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3289 /*
3290  * - LGPL
3291  *
3292  * navbar
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.Navbar
3298  * @extends Roo.bootstrap.Component
3299  * Bootstrap Navbar class
3300
3301  * @constructor
3302  * Create a new Navbar
3303  * @param {Object} config The config object
3304  */
3305
3306
3307 Roo.bootstrap.Navbar = function(config){
3308     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3309     
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3313     
3314     
3315    
3316     // private
3317     navItems : false,
3318     loadMask : false,
3319     
3320     
3321     getAutoCreate : function(){
3322         
3323         
3324         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3325         
3326     },
3327     
3328     initEvents :function ()
3329     {
3330         //Roo.log(this.el.select('.navbar-toggle',true));
3331         this.el.select('.navbar-toggle',true).on('click', function() {
3332            // Roo.log('click');
3333             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3334         }, this);
3335         
3336         var mark = {
3337             tag: "div",
3338             cls:"x-dlg-mask"
3339         }
3340         
3341         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3342         
3343         var size = this.el.getSize();
3344         this.maskEl.setSize(size.width, size.height);
3345         this.maskEl.enableDisplayMode("block");
3346         this.maskEl.hide();
3347         
3348         if(this.loadMask){
3349             this.maskEl.show();
3350         }
3351     },
3352     
3353     
3354     getChildContainer : function()
3355     {
3356         if (this.el.select('.collapse').getCount()) {
3357             return this.el.select('.collapse',true).first();
3358         }
3359         
3360         return this.el;
3361     },
3362     
3363     mask : function()
3364     {
3365         this.maskEl.show();
3366     },
3367     
3368     unmask : function()
3369     {
3370         this.maskEl.hide();
3371     } 
3372     
3373     
3374     
3375     
3376 });
3377
3378
3379
3380  
3381
3382  /*
3383  * - LGPL
3384  *
3385  * navbar
3386  * 
3387  */
3388
3389 /**
3390  * @class Roo.bootstrap.NavSimplebar
3391  * @extends Roo.bootstrap.Navbar
3392  * Bootstrap Sidebar class
3393  *
3394  * @cfg {Boolean} inverse is inverted color
3395  * 
3396  * @cfg {String} type (nav | pills | tabs)
3397  * @cfg {Boolean} arrangement stacked | justified
3398  * @cfg {String} align (left | right) alignment
3399  * 
3400  * @cfg {Boolean} main (true|false) main nav bar? default false
3401  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3402  * 
3403  * @cfg {String} tag (header|footer|nav|div) default is nav 
3404
3405  * 
3406  * 
3407  * 
3408  * @constructor
3409  * Create a new Sidebar
3410  * @param {Object} config The config object
3411  */
3412
3413
3414 Roo.bootstrap.NavSimplebar = function(config){
3415     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3416 };
3417
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3419     
3420     inverse: false,
3421     
3422     type: false,
3423     arrangement: '',
3424     align : false,
3425     
3426     
3427     
3428     main : false,
3429     
3430     
3431     tag : false,
3432     
3433     
3434     getAutoCreate : function(){
3435         
3436         
3437         var cfg = {
3438             tag : this.tag || 'div',
3439             cls : 'navbar'
3440         };
3441           
3442         
3443         cfg.cn = [
3444             {
3445                 cls: 'nav',
3446                 tag : 'ul'
3447             }
3448         ];
3449         
3450          
3451         this.type = this.type || 'nav';
3452         if (['tabs','pills'].indexOf(this.type)!==-1) {
3453             cfg.cn[0].cls += ' nav-' + this.type
3454         
3455         
3456         } else {
3457             if (this.type!=='nav') {
3458                 Roo.log('nav type must be nav/tabs/pills')
3459             }
3460             cfg.cn[0].cls += ' navbar-nav'
3461         }
3462         
3463         
3464         
3465         
3466         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467             cfg.cn[0].cls += ' nav-' + this.arrangement;
3468         }
3469         
3470         
3471         if (this.align === 'right') {
3472             cfg.cn[0].cls += ' navbar-right';
3473         }
3474         
3475         if (this.inverse) {
3476             cfg.cls += ' navbar-inverse';
3477             
3478         }
3479         
3480         
3481         return cfg;
3482     
3483         
3484     }
3485     
3486     
3487     
3488 });
3489
3490
3491
3492  
3493
3494  
3495        /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.NavHeaderbar
3504  * @extends Roo.bootstrap.NavSimplebar
3505  * Bootstrap Sidebar class
3506  *
3507  * @cfg {String} brand what is brand
3508  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509  * @cfg {String} brand_href href of the brand
3510  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3511  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3514  * 
3515  * @constructor
3516  * Create a new Sidebar
3517  * @param {Object} config The config object
3518  */
3519
3520
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3523       
3524 };
3525
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3527     
3528     position: '',
3529     brand: '',
3530     brand_href: false,
3531     srButton : true,
3532     autohide : false,
3533     desktopCenter : false,
3534    
3535     
3536     getAutoCreate : function(){
3537         
3538         var   cfg = {
3539             tag: this.nav || 'nav',
3540             cls: 'navbar',
3541             role: 'navigation',
3542             cn: []
3543         };
3544         
3545         var cn = cfg.cn;
3546         if (this.desktopCenter) {
3547             cn.push({cls : 'container', cn : []});
3548             cn = cn[0].cn;
3549         }
3550         
3551         if(this.srButton){
3552             cn.push({
3553                 tag: 'div',
3554                 cls: 'navbar-header',
3555                 cn: [
3556                     {
3557                         tag: 'button',
3558                         type: 'button',
3559                         cls: 'navbar-toggle',
3560                         'data-toggle': 'collapse',
3561                         cn: [
3562                             {
3563                                 tag: 'span',
3564                                 cls: 'sr-only',
3565                                 html: 'Toggle navigation'
3566                             },
3567                             {
3568                                 tag: 'span',
3569                                 cls: 'icon-bar'
3570                             },
3571                             {
3572                                 tag: 'span',
3573                                 cls: 'icon-bar'
3574                             },
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'icon-bar'
3578                             }
3579                         ]
3580                     }
3581                 ]
3582             });
3583         }
3584         
3585         cn.push({
3586             tag: 'div',
3587             cls: 'collapse navbar-collapse',
3588             cn : []
3589         });
3590         
3591         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3592         
3593         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594             cfg.cls += ' navbar-' + this.position;
3595             
3596             // tag can override this..
3597             
3598             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3599         }
3600         
3601         if (this.brand !== '') {
3602             cn[0].cn.push({
3603                 tag: 'a',
3604                 href: this.brand_href ? this.brand_href : '#',
3605                 cls: 'navbar-brand',
3606                 cn: [
3607                 this.brand
3608                 ]
3609             });
3610         }
3611         
3612         if(this.main){
3613             cfg.cls += ' main-nav';
3614         }
3615         
3616         
3617         return cfg;
3618
3619         
3620     },
3621     getHeaderChildContainer : function()
3622     {
3623         if (this.el.select('.navbar-header').getCount()) {
3624             return this.el.select('.navbar-header',true).first();
3625         }
3626         
3627         return this.getChildContainer();
3628     },
3629     
3630     
3631     initEvents : function()
3632     {
3633         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3634         
3635         if (this.autohide) {
3636             
3637             var prevScroll = 0;
3638             var ft = this.el;
3639             
3640             Roo.get(document).on('scroll',function(e) {
3641                 var ns = Roo.get(document).getScroll().top;
3642                 var os = prevScroll;
3643                 prevScroll = ns;
3644                 
3645                 if(ns > os){
3646                     ft.removeClass('slideDown');
3647                     ft.addClass('slideUp');
3648                     return;
3649                 }
3650                 ft.removeClass('slideUp');
3651                 ft.addClass('slideDown');
3652                  
3653               
3654           },this);
3655         }
3656     }    
3657     
3658 });
3659
3660
3661
3662  
3663
3664  /*
3665  * - LGPL
3666  *
3667  * navbar
3668  * 
3669  */
3670
3671 /**
3672  * @class Roo.bootstrap.NavSidebar
3673  * @extends Roo.bootstrap.Navbar
3674  * Bootstrap Sidebar class
3675  * 
3676  * @constructor
3677  * Create a new Sidebar
3678  * @param {Object} config The config object
3679  */
3680
3681
3682 Roo.bootstrap.NavSidebar = function(config){
3683     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3684 };
3685
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3687     
3688     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3689     
3690     getAutoCreate : function(){
3691         
3692         
3693         return  {
3694             tag: 'div',
3695             cls: 'sidebar sidebar-nav'
3696         };
3697     
3698         
3699     }
3700     
3701     
3702     
3703 });
3704
3705
3706
3707  
3708
3709  /*
3710  * - LGPL
3711  *
3712  * nav group
3713  * 
3714  */
3715
3716 /**
3717  * @class Roo.bootstrap.NavGroup
3718  * @extends Roo.bootstrap.Component
3719  * Bootstrap NavGroup class
3720  * @cfg {String} align (left|right)
3721  * @cfg {Boolean} inverse
3722  * @cfg {String} type (nav|pills|tab) default nav
3723  * @cfg {String} navId - reference Id for navbar.
3724
3725  * 
3726  * @constructor
3727  * Create a new nav group
3728  * @param {Object} config The config object
3729  */
3730
3731 Roo.bootstrap.NavGroup = function(config){
3732     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3733     this.navItems = [];
3734    
3735     Roo.bootstrap.NavGroup.register(this);
3736      this.addEvents({
3737         /**
3738              * @event changed
3739              * Fires when the active item changes
3740              * @param {Roo.bootstrap.NavGroup} this
3741              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3743          */
3744         'changed': true
3745      });
3746     
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3750     
3751     align: '',
3752     inverse: false,
3753     form: false,
3754     type: 'nav',
3755     navId : '',
3756     // private
3757     
3758     navItems : false, 
3759     
3760     getAutoCreate : function()
3761     {
3762         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3763         
3764         cfg = {
3765             tag : 'ul',
3766             cls: 'nav' 
3767         }
3768         
3769         if (['tabs','pills'].indexOf(this.type)!==-1) {
3770             cfg.cls += ' nav-' + this.type
3771         } else {
3772             if (this.type!=='nav') {
3773                 Roo.log('nav type must be nav/tabs/pills')
3774             }
3775             cfg.cls += ' navbar-nav'
3776         }
3777         
3778         if (this.parent().sidebar) {
3779             cfg = {
3780                 tag: 'ul',
3781                 cls: 'dashboard-menu sidebar-menu'
3782             }
3783             
3784             return cfg;
3785         }
3786         
3787         if (this.form === true) {
3788             cfg = {
3789                 tag: 'form',
3790                 cls: 'navbar-form'
3791             }
3792             
3793             if (this.align === 'right') {
3794                 cfg.cls += ' navbar-right';
3795             } else {
3796                 cfg.cls += ' navbar-left';
3797             }
3798         }
3799         
3800         if (this.align === 'right') {
3801             cfg.cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     },
3812     /**
3813     * sets the active Navigation item
3814     * @param {Roo.bootstrap.NavItem} the new current navitem
3815     */
3816     setActiveItem : function(item)
3817     {
3818         var prev = false;
3819         Roo.each(this.navItems, function(v){
3820             if (v == item) {
3821                 return ;
3822             }
3823             if (v.isActive()) {
3824                 v.setActive(false, true);
3825                 prev = v;
3826                 
3827             }
3828             
3829         });
3830
3831         item.setActive(true, true);
3832         this.fireEvent('changed', this, item, prev);
3833         
3834         
3835     },
3836     /**
3837     * gets the active Navigation item
3838     * @return {Roo.bootstrap.NavItem} the current navitem
3839     */
3840     getActive : function()
3841     {
3842         
3843         var prev = false;
3844         Roo.each(this.navItems, function(v){
3845             
3846             if (v.isActive()) {
3847                 prev = v;
3848                 
3849             }
3850             
3851         });
3852         return prev;
3853     },
3854     
3855     indexOfNav : function()
3856     {
3857         
3858         var prev = false;
3859         Roo.each(this.navItems, function(v,i){
3860             
3861             if (v.isActive()) {
3862                 prev = i;
3863                 
3864             }
3865             
3866         });
3867         return prev;
3868     },
3869     /**
3870     * adds a Navigation item
3871     * @param {Roo.bootstrap.NavItem} the navitem to add
3872     */
3873     addItem : function(cfg)
3874     {
3875         var cn = new Roo.bootstrap.NavItem(cfg);
3876         this.register(cn);
3877         cn.parentId = this.id;
3878         cn.onRender(this.el, null);
3879         return cn;
3880     },
3881     /**
3882     * register a Navigation item
3883     * @param {Roo.bootstrap.NavItem} the navitem to add
3884     */
3885     register : function(item)
3886     {
3887         this.navItems.push( item);
3888         item.navId = this.navId;
3889     
3890     },
3891     
3892     /**
3893     * clear all the Navigation item
3894     */
3895    
3896     clearAll : function()
3897     {
3898         this.navItems = [];
3899         this.el.dom.innerHTML = '';
3900     },
3901     
3902     getNavItem: function(tabId)
3903     {
3904         var ret = false;
3905         Roo.each(this.navItems, function(e) {
3906             if (e.tabId == tabId) {
3907                ret =  e;
3908                return false;
3909             }
3910             return true;
3911             
3912         });
3913         return ret;
3914     },
3915     
3916     setActiveNext : function()
3917     {
3918         var i = this.indexOfNav(this.getActive());
3919         if (i > this.navItems.length) {
3920             return;
3921         }
3922         this.setActiveItem(this.navItems[i+1]);
3923     },
3924     setActivePrev : function()
3925     {
3926         var i = this.indexOfNav(this.getActive());
3927         if (i  < 1) {
3928             return;
3929         }
3930         this.setActiveItem(this.navItems[i-1]);
3931     },
3932     clearWasActive : function(except) {
3933         Roo.each(this.navItems, function(e) {
3934             if (e.tabId != except.tabId && e.was_active) {
3935                e.was_active = false;
3936                return false;
3937             }
3938             return true;
3939             
3940         });
3941     },
3942     getWasActive : function ()
3943     {
3944         var r = false;
3945         Roo.each(this.navItems, function(e) {
3946             if (e.was_active) {
3947                r = e;
3948                return false;
3949             }
3950             return true;
3951             
3952         });
3953         return r;
3954     }
3955     
3956     
3957 });
3958
3959  
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3961     
3962     groups: {},
3963      /**
3964     * register a Navigation Group
3965     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3966     */
3967     register : function(navgrp)
3968     {
3969         this.groups[navgrp.navId] = navgrp;
3970         
3971     },
3972     /**
3973     * fetch a Navigation Group based on the navigation ID
3974     * @param {string} the navgroup to add
3975     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3976     */
3977     get: function(navId) {
3978         if (typeof(this.groups[navId]) == 'undefined') {
3979             return false;
3980             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3981         }
3982         return this.groups[navId] ;
3983     }
3984     
3985     
3986     
3987 });
3988
3989  /*
3990  * - LGPL
3991  *
3992  * row
3993  * 
3994  */
3995
3996 /**
3997  * @class Roo.bootstrap.NavItem
3998  * @extends Roo.bootstrap.Component
3999  * Bootstrap Navbar.NavItem class
4000  * @cfg {String} href  link to
4001  * @cfg {String} html content of button
4002  * @cfg {String} badge text inside badge
4003  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004  * @cfg {String} glyphicon name of glyphicon
4005  * @cfg {String} icon name of font awesome icon
4006  * @cfg {Boolean} active Is item active
4007  * @cfg {Boolean} disabled Is item disabled
4008  
4009  * @cfg {Boolean} preventDefault (true | false) default false
4010  * @cfg {String} tabId the tab that this item activates.
4011  * @cfg {String} tagtype (a|span) render as a href or span?
4012  * @cfg {Boolean} animateRef (true|false) link to element default false  
4013   
4014  * @constructor
4015  * Create a new Navbar Item
4016  * @param {Object} config The config object
4017  */
4018 Roo.bootstrap.NavItem = function(config){
4019     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4020     this.addEvents({
4021         // raw events
4022         /**
4023          * @event click
4024          * The raw click event for the entire grid.
4025          * @param {Roo.EventObject} e
4026          */
4027         "click" : true,
4028          /**
4029             * @event changed
4030             * Fires when the active item active state changes
4031             * @param {Roo.bootstrap.NavItem} this
4032             * @param {boolean} state the new state
4033              
4034          */
4035         'changed': true,
4036         /**
4037             * @event scrollto
4038             * Fires when scroll to element
4039             * @param {Roo.bootstrap.NavItem} this
4040             * @param {Object} options
4041             * @param {Roo.EventObject} e
4042              
4043          */
4044         'scrollto': true
4045     });
4046    
4047 };
4048
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4050     
4051     href: false,
4052     html: '',
4053     badge: '',
4054     icon: false,
4055     glyphicon: false,
4056     active: false,
4057     preventDefault : false,
4058     tabId : false,
4059     tagtype : 'a',
4060     disabled : false,
4061     animateRef : false,
4062     was_active : false,
4063     
4064     getAutoCreate : function(){
4065          
4066         var cfg = {
4067             tag: 'li',
4068             cls: 'nav-item'
4069             
4070         }
4071         if (this.active) {
4072             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4073         }
4074         if (this.disabled) {
4075             cfg.cls += ' disabled';
4076         }
4077         
4078         if (this.href || this.html || this.glyphicon || this.icon) {
4079             cfg.cn = [
4080                 {
4081                     tag: this.tagtype,
4082                     href : this.href || "#",
4083                     html: this.html || ''
4084                 }
4085             ];
4086             
4087             if (this.icon) {
4088                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4089             }
4090
4091             if(this.glyphicon) {
4092                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4093             }
4094             
4095             if (this.menu) {
4096                 
4097                 cfg.cn[0].html += " <span class='caret'></span>";
4098              
4099             }
4100             
4101             if (this.badge !== '') {
4102                  
4103                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4104             }
4105         }
4106         
4107         
4108         
4109         return cfg;
4110     },
4111     initEvents: function() 
4112     {
4113         if (typeof (this.menu) != 'undefined') {
4114             this.menu.parentType = this.xtype;
4115             this.menu.triggerEl = this.el;
4116             this.menu = this.addxtype(Roo.apply({}, this.menu));
4117         }
4118         
4119         this.el.select('a',true).on('click', this.onClick, this);
4120         
4121         if(this.tagtype == 'span'){
4122             this.el.select('span',true).on('click', this.onClick, this);
4123         }
4124        
4125         // at this point parent should be available..
4126         this.parent().register(this);
4127     },
4128     
4129     onClick : function(e)
4130     {
4131         if(
4132                 this.preventDefault || 
4133                 this.href == '#' 
4134         ){
4135             
4136             e.preventDefault();
4137         }
4138         
4139         if (this.disabled) {
4140             return;
4141         }
4142         
4143         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144         if (tg && tg.transition) {
4145             Roo.log("waiting for the transitionend");
4146             return;
4147         }
4148         
4149         
4150         
4151         //Roo.log("fire event clicked");
4152         if(this.fireEvent('click', this, e) === false){
4153             return;
4154         };
4155         
4156         if(this.tagtype == 'span'){
4157             return;
4158         }
4159         
4160         //Roo.log(this.href);
4161         var ael = this.el.select('a',true).first();
4162         //Roo.log(ael);
4163         
4164         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167                 return; // ignore... - it's a 'hash' to another page.
4168             }
4169             
4170             e.preventDefault();
4171             this.scrollToElement(e);
4172         }
4173         
4174         
4175         var p =  this.parent();
4176    
4177         if (['tabs','pills'].indexOf(p.type)!==-1) {
4178             if (typeof(p.setActiveItem) !== 'undefined') {
4179                 p.setActiveItem(this);
4180             }
4181         }
4182         
4183         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185             // remove the collapsed menu expand...
4186             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4187         }
4188     },
4189     
4190     isActive: function () {
4191         return this.active
4192     },
4193     setActive : function(state, fire, is_was_active)
4194     {
4195         if (this.active && !state && this.navId) {
4196             this.was_active = true;
4197             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4198             if (nv) {
4199                 nv.clearWasActive(this);
4200             }
4201             
4202         }
4203         this.active = state;
4204         
4205         if (!state ) {
4206             this.el.removeClass('active');
4207         } else if (!this.el.hasClass('active')) {
4208             this.el.addClass('active');
4209         }
4210         if (fire) {
4211             this.fireEvent('changed', this, state);
4212         }
4213         
4214         // show a panel if it's registered and related..
4215         
4216         if (!this.navId || !this.tabId || !state || is_was_active) {
4217             return;
4218         }
4219         
4220         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4221         if (!tg) {
4222             return;
4223         }
4224         var pan = tg.getPanelByName(this.tabId);
4225         if (!pan) {
4226             return;
4227         }
4228         // if we can not flip to new panel - go back to old nav highlight..
4229         if (false == tg.showPanel(pan)) {
4230             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4231             if (nv) {
4232                 var onav = nv.getWasActive();
4233                 if (onav) {
4234                     onav.setActive(true, false, true);
4235                 }
4236             }
4237             
4238         }
4239         
4240         
4241         
4242     },
4243      // this should not be here...
4244     setDisabled : function(state)
4245     {
4246         this.disabled = state;
4247         if (!state ) {
4248             this.el.removeClass('disabled');
4249         } else if (!this.el.hasClass('disabled')) {
4250             this.el.addClass('disabled');
4251         }
4252         
4253     },
4254     
4255     /**
4256      * Fetch the element to display the tooltip on.
4257      * @return {Roo.Element} defaults to this.el
4258      */
4259     tooltipEl : function()
4260     {
4261         return this.el.select('' + this.tagtype + '', true).first();
4262     },
4263     
4264     scrollToElement : function(e)
4265     {
4266         var c = document.body;
4267         
4268         /*
4269          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4270          */
4271         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272             c = document.documentElement;
4273         }
4274         
4275         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4276         
4277         if(!target){
4278             return;
4279         }
4280
4281         var o = target.calcOffsetsTo(c);
4282         
4283         var options = {
4284             target : target,
4285             value : o[1]
4286         }
4287         
4288         this.fireEvent('scrollto', this, options, e);
4289         
4290         Roo.get(c).scrollTo('top', options.value, true);
4291         
4292         return;
4293     }
4294 });
4295  
4296
4297  /*
4298  * - LGPL
4299  *
4300  * sidebar item
4301  *
4302  *  li
4303  *    <span> icon </span>
4304  *    <span> text </span>
4305  *    <span>badge </span>
4306  */
4307
4308 /**
4309  * @class Roo.bootstrap.NavSidebarItem
4310  * @extends Roo.bootstrap.NavItem
4311  * Bootstrap Navbar.NavSidebarItem class
4312  * @constructor
4313  * Create a new Navbar Button
4314  * @param {Object} config The config object
4315  */
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4318     this.addEvents({
4319         // raw events
4320         /**
4321          * @event click
4322          * The raw click event for the entire grid.
4323          * @param {Roo.EventObject} e
4324          */
4325         "click" : true,
4326          /**
4327             * @event changed
4328             * Fires when the active item active state changes
4329             * @param {Roo.bootstrap.NavSidebarItem} this
4330             * @param {boolean} state the new state
4331              
4332          */
4333         'changed': true
4334     });
4335    
4336 };
4337
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4339     
4340     
4341     getAutoCreate : function(){
4342         
4343         
4344         var a = {
4345                 tag: 'a',
4346                 href : this.href || '#',
4347                 cls: '',
4348                 html : '',
4349                 cn : []
4350         };
4351         var cfg = {
4352             tag: 'li',
4353             cls: '',
4354             cn: [ a ]
4355         }
4356         var span = {
4357             tag: 'span',
4358             html : this.html || ''
4359         }
4360         
4361         
4362         if (this.active) {
4363             cfg.cls += ' active';
4364         }
4365         
4366         // left icon..
4367         if (this.glyphicon || this.icon) {
4368             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4369             a.cn.push({ tag : 'i', cls : c }) ;
4370         }
4371         // html..
4372         a.cn.push(span);
4373         // then badge..
4374         if (this.badge !== '') {
4375             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4376         }
4377         // fi
4378         if (this.menu) {
4379             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380             a.cls += 'dropdown-toggle treeview' ;
4381             
4382         }
4383         
4384         
4385         
4386         return cfg;
4387          
4388            
4389     }
4390    
4391      
4392  
4393 });
4394  
4395
4396  /*
4397  * - LGPL
4398  *
4399  * row
4400  * 
4401  */
4402
4403 /**
4404  * @class Roo.bootstrap.Row
4405  * @extends Roo.bootstrap.Component
4406  * Bootstrap Row class (contains columns...)
4407  * 
4408  * @constructor
4409  * Create a new Row
4410  * @param {Object} config The config object
4411  */
4412
4413 Roo.bootstrap.Row = function(config){
4414     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4415 };
4416
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4418     
4419     getAutoCreate : function(){
4420        return {
4421             cls: 'row clearfix'
4422        };
4423     }
4424     
4425     
4426 });
4427
4428  
4429
4430  /*
4431  * - LGPL
4432  *
4433  * element
4434  * 
4435  */
4436
4437 /**
4438  * @class Roo.bootstrap.Element
4439  * @extends Roo.bootstrap.Component
4440  * Bootstrap Element class
4441  * @cfg {String} html contents of the element
4442  * @cfg {String} tag tag of the element
4443  * @cfg {String} cls class of the element
4444  * @cfg {Boolean} preventDefault (true|false) default false
4445  * @cfg {Boolean} clickable (true|false) default false
4446  * 
4447  * @constructor
4448  * Create a new Element
4449  * @param {Object} config The config object
4450  */
4451
4452 Roo.bootstrap.Element = function(config){
4453     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4454     
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * When a element is chick
4460          * @param {Roo.bootstrap.Element} this
4461          * @param {Roo.EventObject} e
4462          */
4463         "click" : true
4464     });
4465 };
4466
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4468     
4469     tag: 'div',
4470     cls: '',
4471     html: '',
4472     preventDefault: false, 
4473     clickable: false,
4474     
4475     getAutoCreate : function(){
4476         
4477         var cfg = {
4478             tag: this.tag,
4479             cls: this.cls,
4480             html: this.html
4481         }
4482         
4483         return cfg;
4484     },
4485     
4486     initEvents: function() 
4487     {
4488         Roo.bootstrap.Element.superclass.initEvents.call(this);
4489         
4490         if(this.clickable){
4491             this.el.on('click', this.onClick, this);
4492         }
4493         
4494     },
4495     
4496     onClick : function(e)
4497     {
4498         if(this.preventDefault){
4499             e.preventDefault();
4500         }
4501         
4502         this.fireEvent('click', this, e);
4503     },
4504     
4505     getValue : function()
4506     {
4507         return this.el.dom.innerHTML;
4508     },
4509     
4510     setValue : function(value)
4511     {
4512         this.el.dom.innerHTML = value;
4513     }
4514    
4515 });
4516
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * pagination
4523  * 
4524  */
4525
4526 /**
4527  * @class Roo.bootstrap.Pagination
4528  * @extends Roo.bootstrap.Component
4529  * Bootstrap Pagination class
4530  * @cfg {String} size xs | sm | md | lg
4531  * @cfg {Boolean} inverse false | true
4532  * 
4533  * @constructor
4534  * Create a new Pagination
4535  * @param {Object} config The config object
4536  */
4537
4538 Roo.bootstrap.Pagination = function(config){
4539     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4540 };
4541
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4543     
4544     cls: false,
4545     size: false,
4546     inverse: false,
4547     
4548     getAutoCreate : function(){
4549         var cfg = {
4550             tag: 'ul',
4551                 cls: 'pagination'
4552         };
4553         if (this.inverse) {
4554             cfg.cls += ' inverse';
4555         }
4556         if (this.html) {
4557             cfg.html=this.html;
4558         }
4559         if (this.cls) {
4560             cfg.cls += " " + this.cls;
4561         }
4562         return cfg;
4563     }
4564    
4565 });
4566
4567  
4568
4569  /*
4570  * - LGPL
4571  *
4572  * Pagination item
4573  * 
4574  */
4575
4576
4577 /**
4578  * @class Roo.bootstrap.PaginationItem
4579  * @extends Roo.bootstrap.Component
4580  * Bootstrap PaginationItem class
4581  * @cfg {String} html text
4582  * @cfg {String} href the link
4583  * @cfg {Boolean} preventDefault (true | false) default true
4584  * @cfg {Boolean} active (true | false) default false
4585  * @cfg {Boolean} disabled default false
4586  * 
4587  * 
4588  * @constructor
4589  * Create a new PaginationItem
4590  * @param {Object} config The config object
4591  */
4592
4593
4594 Roo.bootstrap.PaginationItem = function(config){
4595     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4596     this.addEvents({
4597         // raw events
4598         /**
4599          * @event click
4600          * The raw click event for the entire grid.
4601          * @param {Roo.EventObject} e
4602          */
4603         "click" : true
4604     });
4605 };
4606
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4608     
4609     href : false,
4610     html : false,
4611     preventDefault: true,
4612     active : false,
4613     cls : false,
4614     disabled: false,
4615     
4616     getAutoCreate : function(){
4617         var cfg= {
4618             tag: 'li',
4619             cn: [
4620                 {
4621                     tag : 'a',
4622                     href : this.href ? this.href : '#',
4623                     html : this.html ? this.html : ''
4624                 }
4625             ]
4626         };
4627         
4628         if(this.cls){
4629             cfg.cls = this.cls;
4630         }
4631         
4632         if(this.disabled){
4633             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4634         }
4635         
4636         if(this.active){
4637             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4638         }
4639         
4640         return cfg;
4641     },
4642     
4643     initEvents: function() {
4644         
4645         this.el.on('click', this.onClick, this);
4646         
4647     },
4648     onClick : function(e)
4649     {
4650         Roo.log('PaginationItem on click ');
4651         if(this.preventDefault){
4652             e.preventDefault();
4653         }
4654         
4655         if(this.disabled){
4656             return;
4657         }
4658         
4659         this.fireEvent('click', this, e);
4660     }
4661    
4662 });
4663
4664  
4665
4666  /*
4667  * - LGPL
4668  *
4669  * slider
4670  * 
4671  */
4672
4673
4674 /**
4675  * @class Roo.bootstrap.Slider
4676  * @extends Roo.bootstrap.Component
4677  * Bootstrap Slider class
4678  *    
4679  * @constructor
4680  * Create a new Slider
4681  * @param {Object} config The config object
4682  */
4683
4684 Roo.bootstrap.Slider = function(config){
4685     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4686 };
4687
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4689     
4690     getAutoCreate : function(){
4691         
4692         var cfg = {
4693             tag: 'div',
4694             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4695             cn: [
4696                 {
4697                     tag: 'a',
4698                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4699                 }
4700             ]
4701         }
4702         
4703         return cfg;
4704     }
4705    
4706 });
4707
4708  /*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718  
4719
4720 /**
4721  * @class Roo.grid.ColumnModel
4722  * @extends Roo.util.Observable
4723  * This is the default implementation of a ColumnModel used by the Grid. It defines
4724  * the columns in the grid.
4725  * <br>Usage:<br>
4726  <pre><code>
4727  var colModel = new Roo.grid.ColumnModel([
4728         {header: "Ticker", width: 60, sortable: true, locked: true},
4729         {header: "Company Name", width: 150, sortable: true},
4730         {header: "Market Cap.", width: 100, sortable: true},
4731         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732         {header: "Employees", width: 100, sortable: true, resizable: false}
4733  ]);
4734  </code></pre>
4735  * <p>
4736  
4737  * The config options listed for this class are options which may appear in each
4738  * individual column definition.
4739  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4740  * @constructor
4741  * @param {Object} config An Array of column config objects. See this class's
4742  * config objects for details.
4743 */
4744 Roo.grid.ColumnModel = function(config){
4745         /**
4746      * The config passed into the constructor
4747      */
4748     this.config = config;
4749     this.lookup = {};
4750
4751     // if no id, create one
4752     // if the column does not have a dataIndex mapping,
4753     // map it to the order it is in the config
4754     for(var i = 0, len = config.length; i < len; i++){
4755         var c = config[i];
4756         if(typeof c.dataIndex == "undefined"){
4757             c.dataIndex = i;
4758         }
4759         if(typeof c.renderer == "string"){
4760             c.renderer = Roo.util.Format[c.renderer];
4761         }
4762         if(typeof c.id == "undefined"){
4763             c.id = Roo.id();
4764         }
4765         if(c.editor && c.editor.xtype){
4766             c.editor  = Roo.factory(c.editor, Roo.grid);
4767         }
4768         if(c.editor && c.editor.isFormField){
4769             c.editor = new Roo.grid.GridEditor(c.editor);
4770         }
4771         this.lookup[c.id] = c;
4772     }
4773
4774     /**
4775      * The width of columns which have no width specified (defaults to 100)
4776      * @type Number
4777      */
4778     this.defaultWidth = 100;
4779
4780     /**
4781      * Default sortable of columns which have no sortable specified (defaults to false)
4782      * @type Boolean
4783      */
4784     this.defaultSortable = false;
4785
4786     this.addEvents({
4787         /**
4788              * @event widthchange
4789              * Fires when the width of a column changes.
4790              * @param {ColumnModel} this
4791              * @param {Number} columnIndex The column index
4792              * @param {Number} newWidth The new width
4793              */
4794             "widthchange": true,
4795         /**
4796              * @event headerchange
4797              * Fires when the text of a header changes.
4798              * @param {ColumnModel} this
4799              * @param {Number} columnIndex The column index
4800              * @param {Number} newText The new header text
4801              */
4802             "headerchange": true,
4803         /**
4804              * @event hiddenchange
4805              * Fires when a column is hidden or "unhidden".
4806              * @param {ColumnModel} this
4807              * @param {Number} columnIndex The column index
4808              * @param {Boolean} hidden true if hidden, false otherwise
4809              */
4810             "hiddenchange": true,
4811             /**
4812          * @event columnmoved
4813          * Fires when a column is moved.
4814          * @param {ColumnModel} this
4815          * @param {Number} oldIndex
4816          * @param {Number} newIndex
4817          */
4818         "columnmoved" : true,
4819         /**
4820          * @event columlockchange
4821          * Fires when a column's locked state is changed
4822          * @param {ColumnModel} this
4823          * @param {Number} colIndex
4824          * @param {Boolean} locked true if locked
4825          */
4826         "columnlockchange" : true
4827     });
4828     Roo.grid.ColumnModel.superclass.constructor.call(this);
4829 };
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4831     /**
4832      * @cfg {String} header The header text to display in the Grid view.
4833      */
4834     /**
4835      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837      * specified, the column's index is used as an index into the Record's data Array.
4838      */
4839     /**
4840      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4842      */
4843     /**
4844      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845      * Defaults to the value of the {@link #defaultSortable} property.
4846      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4847      */
4848     /**
4849      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4850      */
4851     /**
4852      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4853      */
4854     /**
4855      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4856      */
4857     /**
4858      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4859      */
4860     /**
4861      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4865      */
4866        /**
4867      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4868      */
4869     /**
4870      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4871      */
4872     /**
4873      * @cfg {String} cursor (Optional)
4874      */
4875     /**
4876      * @cfg {String} tooltip (Optional)
4877      */
4878     /**
4879      * Returns the id of the column at the specified index.
4880      * @param {Number} index The column index
4881      * @return {String} the id
4882      */
4883     getColumnId : function(index){
4884         return this.config[index].id;
4885     },
4886
4887     /**
4888      * Returns the column for a specified id.
4889      * @param {String} id The column id
4890      * @return {Object} the column
4891      */
4892     getColumnById : function(id){
4893         return this.lookup[id];
4894     },
4895
4896     
4897     /**
4898      * Returns the column for a specified dataIndex.
4899      * @param {String} dataIndex The column dataIndex
4900      * @return {Object|Boolean} the column or false if not found
4901      */
4902     getColumnByDataIndex: function(dataIndex){
4903         var index = this.findColumnIndex(dataIndex);
4904         return index > -1 ? this.config[index] : false;
4905     },
4906     
4907     /**
4908      * Returns the index for a specified column id.
4909      * @param {String} id The column id
4910      * @return {Number} the index, or -1 if not found
4911      */
4912     getIndexById : function(id){
4913         for(var i = 0, len = this.config.length; i < len; i++){
4914             if(this.config[i].id == id){
4915                 return i;
4916             }
4917         }
4918         return -1;
4919     },
4920     
4921     /**
4922      * Returns the index for a specified column dataIndex.
4923      * @param {String} dataIndex The column dataIndex
4924      * @return {Number} the index, or -1 if not found
4925      */
4926     
4927     findColumnIndex : function(dataIndex){
4928         for(var i = 0, len = this.config.length; i < len; i++){
4929             if(this.config[i].dataIndex == dataIndex){
4930                 return i;
4931             }
4932         }
4933         return -1;
4934     },
4935     
4936     
4937     moveColumn : function(oldIndex, newIndex){
4938         var c = this.config[oldIndex];
4939         this.config.splice(oldIndex, 1);
4940         this.config.splice(newIndex, 0, c);
4941         this.dataMap = null;
4942         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4943     },
4944
4945     isLocked : function(colIndex){
4946         return this.config[colIndex].locked === true;
4947     },
4948
4949     setLocked : function(colIndex, value, suppressEvent){
4950         if(this.isLocked(colIndex) == value){
4951             return;
4952         }
4953         this.config[colIndex].locked = value;
4954         if(!suppressEvent){
4955             this.fireEvent("columnlockchange", this, colIndex, value);
4956         }
4957     },
4958
4959     getTotalLockedWidth : function(){
4960         var totalWidth = 0;
4961         for(var i = 0; i < this.config.length; i++){
4962             if(this.isLocked(i) && !this.isHidden(i)){
4963                 this.totalWidth += this.getColumnWidth(i);
4964             }
4965         }
4966         return totalWidth;
4967     },
4968
4969     getLockedCount : function(){
4970         for(var i = 0, len = this.config.length; i < len; i++){
4971             if(!this.isLocked(i)){
4972                 return i;
4973             }
4974         }
4975     },
4976
4977     /**
4978      * Returns the number of columns.
4979      * @return {Number}
4980      */
4981     getColumnCount : function(visibleOnly){
4982         if(visibleOnly === true){
4983             var c = 0;
4984             for(var i = 0, len = this.config.length; i < len; i++){
4985                 if(!this.isHidden(i)){
4986                     c++;
4987                 }
4988             }
4989             return c;
4990         }
4991         return this.config.length;
4992     },
4993
4994     /**
4995      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996      * @param {Function} fn
4997      * @param {Object} scope (optional)
4998      * @return {Array} result
4999      */
5000     getColumnsBy : function(fn, scope){
5001         var r = [];
5002         for(var i = 0, len = this.config.length; i < len; i++){
5003             var c = this.config[i];
5004             if(fn.call(scope||this, c, i) === true){
5005                 r[r.length] = c;
5006             }
5007         }
5008         return r;
5009     },
5010
5011     /**
5012      * Returns true if the specified column is sortable.
5013      * @param {Number} col The column index
5014      * @return {Boolean}
5015      */
5016     isSortable : function(col){
5017         if(typeof this.config[col].sortable == "undefined"){
5018             return this.defaultSortable;
5019         }
5020         return this.config[col].sortable;
5021     },
5022
5023     /**
5024      * Returns the rendering (formatting) function defined for the column.
5025      * @param {Number} col The column index.
5026      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5027      */
5028     getRenderer : function(col){
5029         if(!this.config[col].renderer){
5030             return Roo.grid.ColumnModel.defaultRenderer;
5031         }
5032         return this.config[col].renderer;
5033     },
5034
5035     /**
5036      * Sets the rendering (formatting) function for a column.
5037      * @param {Number} col The column index
5038      * @param {Function} fn The function to use to process the cell's raw data
5039      * to return HTML markup for the grid view. The render function is called with
5040      * the following parameters:<ul>
5041      * <li>Data value.</li>
5042      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043      * <li>css A CSS style string to apply to the table cell.</li>
5044      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046      * <li>Row index</li>
5047      * <li>Column index</li>
5048      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5049      */
5050     setRenderer : function(col, fn){
5051         this.config[col].renderer = fn;
5052     },
5053
5054     /**
5055      * Returns the width for the specified column.
5056      * @param {Number} col The column index
5057      * @return {Number}
5058      */
5059     getColumnWidth : function(col){
5060         return this.config[col].width * 1 || this.defaultWidth;
5061     },
5062
5063     /**
5064      * Sets the width for a column.
5065      * @param {Number} col The column index
5066      * @param {Number} width The new width
5067      */
5068     setColumnWidth : function(col, width, suppressEvent){
5069         this.config[col].width = width;
5070         this.totalWidth = null;
5071         if(!suppressEvent){
5072              this.fireEvent("widthchange", this, col, width);
5073         }
5074     },
5075
5076     /**
5077      * Returns the total width of all columns.
5078      * @param {Boolean} includeHidden True to include hidden column widths
5079      * @return {Number}
5080      */
5081     getTotalWidth : function(includeHidden){
5082         if(!this.totalWidth){
5083             this.totalWidth = 0;
5084             for(var i = 0, len = this.config.length; i < len; i++){
5085                 if(includeHidden || !this.isHidden(i)){
5086                     this.totalWidth += this.getColumnWidth(i);
5087                 }
5088             }
5089         }
5090         return this.totalWidth;
5091     },
5092
5093     /**
5094      * Returns the header for the specified column.
5095      * @param {Number} col The column index
5096      * @return {String}
5097      */
5098     getColumnHeader : function(col){
5099         return this.config[col].header;
5100     },
5101
5102     /**
5103      * Sets the header for a column.
5104      * @param {Number} col The column index
5105      * @param {String} header The new header
5106      */
5107     setColumnHeader : function(col, header){
5108         this.config[col].header = header;
5109         this.fireEvent("headerchange", this, col, header);
5110     },
5111
5112     /**
5113      * Returns the tooltip for the specified column.
5114      * @param {Number} col The column index
5115      * @return {String}
5116      */
5117     getColumnTooltip : function(col){
5118             return this.config[col].tooltip;
5119     },
5120     /**
5121      * Sets the tooltip for a column.
5122      * @param {Number} col The column index
5123      * @param {String} tooltip The new tooltip
5124      */
5125     setColumnTooltip : function(col, tooltip){
5126             this.config[col].tooltip = tooltip;
5127     },
5128
5129     /**
5130      * Returns the dataIndex for the specified column.
5131      * @param {Number} col The column index
5132      * @return {Number}
5133      */
5134     getDataIndex : function(col){
5135         return this.config[col].dataIndex;
5136     },
5137
5138     /**
5139      * Sets the dataIndex for a column.
5140      * @param {Number} col The column index
5141      * @param {Number} dataIndex The new dataIndex
5142      */
5143     setDataIndex : function(col, dataIndex){
5144         this.config[col].dataIndex = dataIndex;
5145     },
5146
5147     
5148     
5149     /**
5150      * Returns true if the cell is editable.
5151      * @param {Number} colIndex The column index
5152      * @param {Number} rowIndex The row index
5153      * @return {Boolean}
5154      */
5155     isCellEditable : function(colIndex, rowIndex){
5156         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5157     },
5158
5159     /**
5160      * Returns the editor defined for the cell/column.
5161      * return false or null to disable editing.
5162      * @param {Number} colIndex The column index
5163      * @param {Number} rowIndex The row index
5164      * @return {Object}
5165      */
5166     getCellEditor : function(colIndex, rowIndex){
5167         return this.config[colIndex].editor;
5168     },
5169
5170     /**
5171      * Sets if a column is editable.
5172      * @param {Number} col The column index
5173      * @param {Boolean} editable True if the column is editable
5174      */
5175     setEditable : function(col, editable){
5176         this.config[col].editable = editable;
5177     },
5178
5179
5180     /**
5181      * Returns true if the column is hidden.
5182      * @param {Number} colIndex The column index
5183      * @return {Boolean}
5184      */
5185     isHidden : function(colIndex){
5186         return this.config[colIndex].hidden;
5187     },
5188
5189
5190     /**
5191      * Returns true if the column width cannot be changed
5192      */
5193     isFixed : function(colIndex){
5194         return this.config[colIndex].fixed;
5195     },
5196
5197     /**
5198      * Returns true if the column can be resized
5199      * @return {Boolean}
5200      */
5201     isResizable : function(colIndex){
5202         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5203     },
5204     /**
5205      * Sets if a column is hidden.
5206      * @param {Number} colIndex The column index
5207      * @param {Boolean} hidden True if the column is hidden
5208      */
5209     setHidden : function(colIndex, hidden){
5210         this.config[colIndex].hidden = hidden;
5211         this.totalWidth = null;
5212         this.fireEvent("hiddenchange", this, colIndex, hidden);
5213     },
5214
5215     /**
5216      * Sets the editor for a column.
5217      * @param {Number} col The column index
5218      * @param {Object} editor The editor object
5219      */
5220     setEditor : function(col, editor){
5221         this.config[col].editor = editor;
5222     }
5223 });
5224
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226         if(typeof value == "string" && value.length < 1){
5227             return "&#160;";
5228         }
5229         return value;
5230 };
5231
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5234 /*
5235  * Based on:
5236  * Ext JS Library 1.1.1
5237  * Copyright(c) 2006-2007, Ext JS, LLC.
5238  *
5239  * Originally Released Under LGPL - original licence link has changed is not relivant.
5240  *
5241  * Fork - LGPL
5242  * <script type="text/javascript">
5243  */
5244  
5245 /**
5246  * @class Roo.LoadMask
5247  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5248  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5250  * element's UpdateManager load indicator and will be destroyed after the initial load.
5251  * @constructor
5252  * Create a new LoadMask
5253  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254  * @param {Object} config The config object
5255  */
5256 Roo.LoadMask = function(el, config){
5257     this.el = Roo.get(el);
5258     Roo.apply(this, config);
5259     if(this.store){
5260         this.store.on('beforeload', this.onBeforeLoad, this);
5261         this.store.on('load', this.onLoad, this);
5262         this.store.on('loadexception', this.onLoadException, this);
5263         this.removeMask = false;
5264     }else{
5265         var um = this.el.getUpdateManager();
5266         um.showLoadIndicator = false; // disable the default indicator
5267         um.on('beforeupdate', this.onBeforeLoad, this);
5268         um.on('update', this.onLoad, this);
5269         um.on('failure', this.onLoad, this);
5270         this.removeMask = true;
5271     }
5272 };
5273
5274 Roo.LoadMask.prototype = {
5275     /**
5276      * @cfg {Boolean} removeMask
5277      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5279      */
5280     /**
5281      * @cfg {String} msg
5282      * The text to display in a centered loading message box (defaults to 'Loading...')
5283      */
5284     msg : 'Loading...',
5285     /**
5286      * @cfg {String} msgCls
5287      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5288      */
5289     msgCls : 'x-mask-loading',
5290
5291     /**
5292      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5293      * @type Boolean
5294      */
5295     disabled: false,
5296
5297     /**
5298      * Disables the mask to prevent it from being displayed
5299      */
5300     disable : function(){
5301        this.disabled = true;
5302     },
5303
5304     /**
5305      * Enables the mask so that it can be displayed
5306      */
5307     enable : function(){
5308         this.disabled = false;
5309     },
5310     
5311     onLoadException : function()
5312     {
5313         Roo.log(arguments);
5314         
5315         if (typeof(arguments[3]) != 'undefined') {
5316             Roo.MessageBox.alert("Error loading",arguments[3]);
5317         } 
5318         /*
5319         try {
5320             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5322             }   
5323         } catch(e) {
5324             
5325         }
5326         */
5327     
5328         
5329         
5330         this.el.unmask(this.removeMask);
5331     },
5332     // private
5333     onLoad : function()
5334     {
5335         this.el.unmask(this.removeMask);
5336     },
5337
5338     // private
5339     onBeforeLoad : function(){
5340         if(!this.disabled){
5341             this.el.mask(this.msg, this.msgCls);
5342         }
5343     },
5344
5345     // private
5346     destroy : function(){
5347         if(this.store){
5348             this.store.un('beforeload', this.onBeforeLoad, this);
5349             this.store.un('load', this.onLoad, this);
5350             this.store.un('loadexception', this.onLoadException, this);
5351         }else{
5352             var um = this.el.getUpdateManager();
5353             um.un('beforeupdate', this.onBeforeLoad, this);
5354             um.un('update', this.onLoad, this);
5355             um.un('failure', this.onLoad, this);
5356         }
5357     }
5358 };/*
5359  * - LGPL
5360  *
5361  * table
5362  * 
5363  */
5364
5365 /**
5366  * @class Roo.bootstrap.Table
5367  * @extends Roo.bootstrap.Component
5368  * Bootstrap Table class
5369  * @cfg {String} cls table class
5370  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371  * @cfg {String} bgcolor Specifies the background color for a table
5372  * @cfg {Number} border Specifies whether the table cells should have borders or not
5373  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374  * @cfg {Number} cellspacing Specifies the space between cells
5375  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377  * @cfg {String} sortable Specifies that the table should be sortable
5378  * @cfg {String} summary Specifies a summary of the content of a table
5379  * @cfg {Number} width Specifies the width of a table
5380  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5381  * 
5382  * @cfg {boolean} striped Should the rows be alternative striped
5383  * @cfg {boolean} bordered Add borders to the table
5384  * @cfg {boolean} hover Add hover highlighting
5385  * @cfg {boolean} condensed Format condensed
5386  * @cfg {boolean} responsive Format condensed
5387  * @cfg {Boolean} loadMask (true|false) default false
5388  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5389  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5390  * @cfg {Boolean} rowSelection (true|false) default false
5391  * @cfg {Boolean} cellSelection (true|false) default false
5392  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5393  
5394  * 
5395  * @constructor
5396  * Create a new Table
5397  * @param {Object} config The config object
5398  */
5399
5400 Roo.bootstrap.Table = function(config){
5401     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5402     
5403     // BC...
5404     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5405     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5406     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5407     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5408     
5409     
5410     if (this.sm) {
5411         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5412         this.sm = this.selModel;
5413         this.sm.xmodule = this.xmodule || false;
5414     }
5415     if (this.cm && typeof(this.cm.config) == 'undefined') {
5416         this.colModel = new Roo.grid.ColumnModel(this.cm);
5417         this.cm = this.colModel;
5418         this.cm.xmodule = this.xmodule || false;
5419     }
5420     if (this.store) {
5421         this.store= Roo.factory(this.store, Roo.data);
5422         this.ds = this.store;
5423         this.ds.xmodule = this.xmodule || false;
5424          
5425     }
5426     if (this.footer && this.store) {
5427         this.footer.dataSource = this.ds;
5428         this.footer = Roo.factory(this.footer);
5429     }
5430     
5431     /** @private */
5432     this.addEvents({
5433         /**
5434          * @event cellclick
5435          * Fires when a cell is clicked
5436          * @param {Roo.bootstrap.Table} this
5437          * @param {Roo.Element} el
5438          * @param {Number} rowIndex
5439          * @param {Number} columnIndex
5440          * @param {Roo.EventObject} e
5441          */
5442         "cellclick" : true,
5443         /**
5444          * @event celldblclick
5445          * Fires when a cell is double clicked
5446          * @param {Roo.bootstrap.Table} this
5447          * @param {Roo.Element} el
5448          * @param {Number} rowIndex
5449          * @param {Number} columnIndex
5450          * @param {Roo.EventObject} e
5451          */
5452         "celldblclick" : true,
5453         /**
5454          * @event rowclick
5455          * Fires when a row is clicked
5456          * @param {Roo.bootstrap.Table} this
5457          * @param {Roo.Element} el
5458          * @param {Number} rowIndex
5459          * @param {Roo.EventObject} e
5460          */
5461         "rowclick" : true,
5462         /**
5463          * @event rowdblclick
5464          * Fires when a row is double clicked
5465          * @param {Roo.bootstrap.Table} this
5466          * @param {Roo.Element} el
5467          * @param {Number} rowIndex
5468          * @param {Roo.EventObject} e
5469          */
5470         "rowdblclick" : true,
5471         /**
5472          * @event mouseover
5473          * Fires when a mouseover occur
5474          * @param {Roo.bootstrap.Table} this
5475          * @param {Roo.Element} el
5476          * @param {Number} rowIndex
5477          * @param {Number} columnIndex
5478          * @param {Roo.EventObject} e
5479          */
5480         "mouseover" : true,
5481         /**
5482          * @event mouseout
5483          * Fires when a mouseout occur
5484          * @param {Roo.bootstrap.Table} this
5485          * @param {Roo.Element} el
5486          * @param {Number} rowIndex
5487          * @param {Number} columnIndex
5488          * @param {Roo.EventObject} e
5489          */
5490         "mouseout" : true,
5491         /**
5492          * @event rowclass
5493          * Fires when a row is rendered, so you can change add a style to it.
5494          * @param {Roo.bootstrap.Table} this
5495          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5496          */
5497         'rowclass' : true,
5498           /**
5499          * @event rowsrendered
5500          * Fires when all the  rows have been rendered
5501          * @param {Roo.bootstrap.Table} this
5502          */
5503         'rowsrendered' : true
5504         
5505     });
5506 };
5507
5508 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5509     
5510     cls: false,
5511     align: false,
5512     bgcolor: false,
5513     border: false,
5514     cellpadding: false,
5515     cellspacing: false,
5516     frame: false,
5517     rules: false,
5518     sortable: false,
5519     summary: false,
5520     width: false,
5521     striped : false,
5522     bordered: false,
5523     hover:  false,
5524     condensed : false,
5525     responsive : false,
5526     sm : false,
5527     cm : false,
5528     store : false,
5529     loadMask : false,
5530     footerShow : true,
5531     headerShow : true,
5532   
5533     rowSelection : false,
5534     cellSelection : false,
5535     layout : false,
5536     
5537     // Roo.Element - the tbody
5538     mainBody: false, 
5539     
5540     getAutoCreate : function(){
5541         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5542         
5543         cfg = {
5544             tag: 'table',
5545             cls : 'table',
5546             cn : []
5547         }
5548             
5549         if (this.striped) {
5550             cfg.cls += ' table-striped';
5551         }
5552         
5553         if (this.hover) {
5554             cfg.cls += ' table-hover';
5555         }
5556         if (this.bordered) {
5557             cfg.cls += ' table-bordered';
5558         }
5559         if (this.condensed) {
5560             cfg.cls += ' table-condensed';
5561         }
5562         if (this.responsive) {
5563             cfg.cls += ' table-responsive';
5564         }
5565         
5566         if (this.cls) {
5567             cfg.cls+=  ' ' +this.cls;
5568         }
5569         
5570         // this lot should be simplifed...
5571         
5572         if (this.align) {
5573             cfg.align=this.align;
5574         }
5575         if (this.bgcolor) {
5576             cfg.bgcolor=this.bgcolor;
5577         }
5578         if (this.border) {
5579             cfg.border=this.border;
5580         }
5581         if (this.cellpadding) {
5582             cfg.cellpadding=this.cellpadding;
5583         }
5584         if (this.cellspacing) {
5585             cfg.cellspacing=this.cellspacing;
5586         }
5587         if (this.frame) {
5588             cfg.frame=this.frame;
5589         }
5590         if (this.rules) {
5591             cfg.rules=this.rules;
5592         }
5593         if (this.sortable) {
5594             cfg.sortable=this.sortable;
5595         }
5596         if (this.summary) {
5597             cfg.summary=this.summary;
5598         }
5599         if (this.width) {
5600             cfg.width=this.width;
5601         }
5602         if (this.layout) {
5603             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5604         }
5605         
5606         if(this.store || this.cm){
5607             if(this.headerShow){
5608                 cfg.cn.push(this.renderHeader());
5609             }
5610             
5611             cfg.cn.push(this.renderBody());
5612             
5613             if(this.footerShow){
5614                 cfg.cn.push(this.renderFooter());
5615             }
5616             
5617             cfg.cls+=  ' TableGrid';
5618         }
5619         
5620         return { cn : [ cfg ] };
5621     },
5622     
5623     initEvents : function()
5624     {   
5625         if(!this.store || !this.cm){
5626             return;
5627         }
5628         
5629         //Roo.log('initEvents with ds!!!!');
5630         
5631         this.mainBody = this.el.select('tbody', true).first();
5632         
5633         
5634         var _this = this;
5635         
5636         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5637             e.on('click', _this.sort, _this);
5638         });
5639         
5640         this.el.on("click", this.onClick, this);
5641         this.el.on("dblclick", this.onDblClick, this);
5642         
5643         // why is this done????? = it breaks dialogs??
5644         //this.parent().el.setStyle('position', 'relative');
5645         
5646         
5647         if (this.footer) {
5648             this.footer.parentId = this.id;
5649             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5650         }
5651         
5652         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5653         
5654         this.store.on('load', this.onLoad, this);
5655         this.store.on('beforeload', this.onBeforeLoad, this);
5656         this.store.on('update', this.onUpdate, this);
5657         this.store.on('add', this.onAdd, this);
5658         
5659     },
5660     
5661     onMouseover : function(e, el)
5662     {
5663         var cell = Roo.get(el);
5664         
5665         if(!cell){
5666             return;
5667         }
5668         
5669         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5670             cell = cell.findParent('td', false, true);
5671         }
5672         
5673         var row = cell.findParent('tr', false, true);
5674         var cellIndex = cell.dom.cellIndex;
5675         var rowIndex = row.dom.rowIndex - 1; // start from 0
5676         
5677         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5678         
5679     },
5680     
5681     onMouseout : function(e, el)
5682     {
5683         var cell = Roo.get(el);
5684         
5685         if(!cell){
5686             return;
5687         }
5688         
5689         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5690             cell = cell.findParent('td', false, true);
5691         }
5692         
5693         var row = cell.findParent('tr', false, true);
5694         var cellIndex = cell.dom.cellIndex;
5695         var rowIndex = row.dom.rowIndex - 1; // start from 0
5696         
5697         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5698         
5699     },
5700     
5701     onClick : function(e, el)
5702     {
5703         var cell = Roo.get(el);
5704         
5705         if(!cell || (!this.cellSelection && !this.rowSelection)){
5706             return;
5707         }
5708         
5709         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5710             cell = cell.findParent('td', false, true);
5711         }
5712         
5713         if(!cell || typeof(cell) == 'undefined'){
5714             return;
5715         }
5716         
5717         var row = cell.findParent('tr', false, true);
5718         
5719         if(!row || typeof(row) == 'undefined'){
5720             return;
5721         }
5722         
5723         var cellIndex = cell.dom.cellIndex;
5724         var rowIndex = this.getRowIndex(row);
5725         
5726         // why??? - should these not be based on SelectionModel?
5727         if(this.cellSelection){
5728             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5729         }
5730         
5731         if(this.rowSelection){
5732             this.fireEvent('rowclick', this, row, rowIndex, e);
5733         }
5734         
5735         
5736     },
5737     
5738     onDblClick : function(e,el)
5739     {
5740         var cell = Roo.get(el);
5741         
5742         if(!cell || (!this.CellSelection && !this.RowSelection)){
5743             return;
5744         }
5745         
5746         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5747             cell = cell.findParent('td', false, true);
5748         }
5749         
5750         if(!cell || typeof(cell) == 'undefined'){
5751             return;
5752         }
5753         
5754         var row = cell.findParent('tr', false, true);
5755         
5756         if(!row || typeof(row) == 'undefined'){
5757             return;
5758         }
5759         
5760         var cellIndex = cell.dom.cellIndex;
5761         var rowIndex = this.getRowIndex(row);
5762         
5763         if(this.CellSelection){
5764             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5765         }
5766         
5767         if(this.RowSelection){
5768             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5769         }
5770     },
5771     
5772     sort : function(e,el)
5773     {
5774         var col = Roo.get(el);
5775         
5776         if(!col.hasClass('sortable')){
5777             return;
5778         }
5779         
5780         var sort = col.attr('sort');
5781         var dir = 'ASC';
5782         
5783         if(col.hasClass('glyphicon-arrow-up')){
5784             dir = 'DESC';
5785         }
5786         
5787         this.store.sortInfo = {field : sort, direction : dir};
5788         
5789         if (this.footer) {
5790             Roo.log("calling footer first");
5791             this.footer.onClick('first');
5792         } else {
5793         
5794             this.store.load({ params : { start : 0 } });
5795         }
5796     },
5797     
5798     renderHeader : function()
5799     {
5800         var header = {
5801             tag: 'thead',
5802             cn : []
5803         };
5804         
5805         var cm = this.cm;
5806         
5807         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5808             
5809             var config = cm.config[i];
5810             
5811             var c = {
5812                 tag: 'th',
5813                 style : '',
5814                 html: cm.getColumnHeader(i)
5815             };
5816             
5817             var hh = '';
5818             
5819             if(typeof(config.lgHeader) != 'undefined'){
5820                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5821             }
5822             
5823             if(typeof(config.mdHeader) != 'undefined'){
5824                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5825             }
5826             
5827             if(typeof(config.smHeader) != 'undefined'){
5828                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5829             }
5830             
5831             if(typeof(config.xsHeader) != 'undefined'){
5832                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5833             }
5834             
5835             if(hh.length){
5836                 c.html = hh;
5837             }
5838             
5839             if(typeof(config.tooltip) != 'undefined'){
5840                 c.tooltip = config.tooltip;
5841             }
5842             
5843             if(typeof(config.colspan) != 'undefined'){
5844                 c.colspan = config.colspan;
5845             }
5846             
5847             if(typeof(config.hidden) != 'undefined' && config.hidden){
5848                 c.style += ' display:none;';
5849             }
5850             
5851             if(typeof(config.dataIndex) != 'undefined'){
5852                 c.sort = config.dataIndex;
5853             }
5854             
5855             if(typeof(config.sortable) != 'undefined' && config.sortable){
5856                 c.cls = 'sortable';
5857             }
5858             
5859             if(typeof(config.align) != 'undefined' && config.align.length){
5860                 c.style += ' text-align:' + config.align + ';';
5861             }
5862             
5863             if(typeof(config.width) != 'undefined'){
5864                 c.style += ' width:' + config.width + 'px;';
5865             }
5866             
5867             if(typeof(config.cls) != 'undefined'){
5868                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5869             }
5870             
5871             header.cn.push(c)
5872         }
5873         
5874         return header;
5875     },
5876     
5877     renderBody : function()
5878     {
5879         var body = {
5880             tag: 'tbody',
5881             cn : [
5882                 {
5883                     tag: 'tr',
5884                     cn : [
5885                         {
5886                             tag : 'td',
5887                             colspan :  this.cm.getColumnCount()
5888                         }
5889                     ]
5890                 }
5891             ]
5892         };
5893         
5894         return body;
5895     },
5896     
5897     renderFooter : function()
5898     {
5899         var footer = {
5900             tag: 'tfoot',
5901             cn : [
5902                 {
5903                     tag: 'tr',
5904                     cn : [
5905                         {
5906                             tag : 'td',
5907                             colspan :  this.cm.getColumnCount()
5908                         }
5909                     ]
5910                 }
5911             ]
5912         };
5913         
5914         return footer;
5915     },
5916     
5917     
5918     
5919     onLoad : function()
5920     {
5921         Roo.log('ds onload');
5922         this.clear();
5923         
5924         var _this = this;
5925         var cm = this.cm;
5926         var ds = this.store;
5927         
5928         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5929             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5930             
5931             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5932                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5933             }
5934             
5935             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5936                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5937             }
5938         });
5939         
5940         var tbody =  this.mainBody;
5941               
5942         if(ds.getCount() > 0){
5943             ds.data.each(function(d,rowIndex){
5944                 var row =  this.renderRow(cm, ds, rowIndex);
5945                 
5946                 tbody.createChild(row);
5947                 
5948                 var _this = this;
5949                 
5950                 if(row.cellObjects.length){
5951                     Roo.each(row.cellObjects, function(r){
5952                         _this.renderCellObject(r);
5953                     })
5954                 }
5955                 
5956             }, this);
5957         }
5958         
5959         Roo.each(this.el.select('tbody td', true).elements, function(e){
5960             e.on('mouseover', _this.onMouseover, _this);
5961         });
5962         
5963         Roo.each(this.el.select('tbody td', true).elements, function(e){
5964             e.on('mouseout', _this.onMouseout, _this);
5965         });
5966         this.fireEvent('rowsrendered', this);
5967         //if(this.loadMask){
5968         //    this.maskEl.hide();
5969         //}
5970     },
5971     
5972     
5973     onUpdate : function(ds,record)
5974     {
5975         this.refreshRow(record);
5976     },
5977     
5978     onRemove : function(ds, record, index, isUpdate){
5979         if(isUpdate !== true){
5980             this.fireEvent("beforerowremoved", this, index, record);
5981         }
5982         var bt = this.mainBody.dom;
5983         
5984         var rows = this.el.select('tbody > tr', true).elements;
5985         
5986         if(typeof(rows[index]) != 'undefined'){
5987             bt.removeChild(rows[index].dom);
5988         }
5989         
5990 //        if(bt.rows[index]){
5991 //            bt.removeChild(bt.rows[index]);
5992 //        }
5993         
5994         if(isUpdate !== true){
5995             //this.stripeRows(index);
5996             //this.syncRowHeights(index, index);
5997             //this.layout();
5998             this.fireEvent("rowremoved", this, index, record);
5999         }
6000     },
6001     
6002     onAdd : function(ds, records, rowIndex)
6003     {
6004         //Roo.log('on Add called');
6005         // - note this does not handle multiple adding very well..
6006         var bt = this.mainBody.dom;
6007         for (var i =0 ; i < records.length;i++) {
6008             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6009             //Roo.log(records[i]);
6010             //Roo.log(this.store.getAt(rowIndex+i));
6011             this.insertRow(this.store, rowIndex + i, false);
6012             return;
6013         }
6014         
6015     },
6016     
6017     
6018     refreshRow : function(record){
6019         var ds = this.store, index;
6020         if(typeof record == 'number'){
6021             index = record;
6022             record = ds.getAt(index);
6023         }else{
6024             index = ds.indexOf(record);
6025         }
6026         this.insertRow(ds, index, true);
6027         this.onRemove(ds, record, index+1, true);
6028         //this.syncRowHeights(index, index);
6029         //this.layout();
6030         this.fireEvent("rowupdated", this, index, record);
6031     },
6032     
6033     insertRow : function(dm, rowIndex, isUpdate){
6034         
6035         if(!isUpdate){
6036             this.fireEvent("beforerowsinserted", this, rowIndex);
6037         }
6038             //var s = this.getScrollState();
6039         var row = this.renderRow(this.cm, this.store, rowIndex);
6040         // insert before rowIndex..
6041         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6042         
6043         var _this = this;
6044                 
6045         if(row.cellObjects.length){
6046             Roo.each(row.cellObjects, function(r){
6047                 _this.renderCellObject(r);
6048             })
6049         }
6050             
6051         if(!isUpdate){
6052             this.fireEvent("rowsinserted", this, rowIndex);
6053             //this.syncRowHeights(firstRow, lastRow);
6054             //this.stripeRows(firstRow);
6055             //this.layout();
6056         }
6057         
6058     },
6059     
6060     
6061     getRowDom : function(rowIndex)
6062     {
6063         var rows = this.el.select('tbody > tr', true).elements;
6064         
6065         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6066         
6067     },
6068     // returns the object tree for a tr..
6069   
6070     
6071     renderRow : function(cm, ds, rowIndex) 
6072     {
6073         
6074         var d = ds.getAt(rowIndex);
6075         
6076         var row = {
6077             tag : 'tr',
6078             cn : []
6079         };
6080             
6081         var cellObjects = [];
6082         
6083         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6084             var config = cm.config[i];
6085             
6086             var renderer = cm.getRenderer(i);
6087             var value = '';
6088             var id = false;
6089             
6090             if(typeof(renderer) !== 'undefined'){
6091                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6092             }
6093             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6094             // and are rendered into the cells after the row is rendered - using the id for the element.
6095             
6096             if(typeof(value) === 'object'){
6097                 id = Roo.id();
6098                 cellObjects.push({
6099                     container : id,
6100                     cfg : value 
6101                 })
6102             }
6103             
6104             var rowcfg = {
6105                 record: d,
6106                 rowIndex : rowIndex,
6107                 colIndex : i,
6108                 rowClass : ''
6109             }
6110
6111             this.fireEvent('rowclass', this, rowcfg);
6112             
6113             var td = {
6114                 tag: 'td',
6115                 cls : rowcfg.rowClass,
6116                 style: '',
6117                 html: (typeof(value) === 'object') ? '' : value
6118             };
6119             
6120             if (id) {
6121                 td.id = id;
6122             }
6123             
6124             if(typeof(config.colspan) != 'undefined'){
6125                 td.colspan = config.colspan;
6126             }
6127             
6128             if(typeof(config.hidden) != 'undefined' && config.hidden){
6129                 td.style += ' display:none;';
6130             }
6131             
6132             if(typeof(config.align) != 'undefined' && config.align.length){
6133                 td.style += ' text-align:' + config.align + ';';
6134             }
6135             
6136             if(typeof(config.width) != 'undefined'){
6137                 td.style += ' width:' +  config.width + 'px;';
6138             }
6139             
6140             if(typeof(config.cursor) != 'undefined'){
6141                 td.style += ' cursor:' +  config.cursor + ';';
6142             }
6143             
6144             if(typeof(config.cls) != 'undefined'){
6145                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6146             }
6147              
6148             row.cn.push(td);
6149            
6150         }
6151         
6152         row.cellObjects = cellObjects;
6153         
6154         return row;
6155           
6156     },
6157     
6158     
6159     
6160     onBeforeLoad : function()
6161     {
6162         //Roo.log('ds onBeforeLoad');
6163         
6164         //this.clear();
6165         
6166         //if(this.loadMask){
6167         //    this.maskEl.show();
6168         //}
6169     },
6170      /**
6171      * Remove all rows
6172      */
6173     clear : function()
6174     {
6175         this.el.select('tbody', true).first().dom.innerHTML = '';
6176     },
6177     /**
6178      * Show or hide a row.
6179      * @param {Number} rowIndex to show or hide
6180      * @param {Boolean} state hide
6181      */
6182     setRowVisibility : function(rowIndex, state)
6183     {
6184         var bt = this.mainBody.dom;
6185         
6186         var rows = this.el.select('tbody > tr', true).elements;
6187         
6188         if(typeof(rows[rowIndex]) == 'undefined'){
6189             return;
6190         }
6191         rows[rowIndex].dom.style.display = state ? '' : 'none';
6192     },
6193     
6194     
6195     getSelectionModel : function(){
6196         if(!this.selModel){
6197             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6198         }
6199         return this.selModel;
6200     },
6201     /*
6202      * Render the Roo.bootstrap object from renderder
6203      */
6204     renderCellObject : function(r)
6205     {
6206         var _this = this;
6207         
6208         var t = r.cfg.render(r.container);
6209         
6210         if(r.cfg.cn){
6211             Roo.each(r.cfg.cn, function(c){
6212                 var child = {
6213                     container: t.getChildContainer(),
6214                     cfg: c
6215                 }
6216                 _this.renderCellObject(child);
6217             })
6218         }
6219     },
6220     
6221     getRowIndex : function(row)
6222     {
6223         var rowIndex = -1;
6224         
6225         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6226             if(el != row){
6227                 return;
6228             }
6229             
6230             rowIndex = index;
6231         });
6232         
6233         return rowIndex;
6234     }
6235    
6236 });
6237
6238  
6239
6240  /*
6241  * - LGPL
6242  *
6243  * table cell
6244  * 
6245  */
6246
6247 /**
6248  * @class Roo.bootstrap.TableCell
6249  * @extends Roo.bootstrap.Component
6250  * Bootstrap TableCell class
6251  * @cfg {String} html cell contain text
6252  * @cfg {String} cls cell class
6253  * @cfg {String} tag cell tag (td|th) default td
6254  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6255  * @cfg {String} align Aligns the content in a cell
6256  * @cfg {String} axis Categorizes cells
6257  * @cfg {String} bgcolor Specifies the background color of a cell
6258  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6259  * @cfg {Number} colspan Specifies the number of columns a cell should span
6260  * @cfg {String} headers Specifies one or more header cells a cell is related to
6261  * @cfg {Number} height Sets the height of a cell
6262  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6263  * @cfg {Number} rowspan Sets the number of rows a cell should span
6264  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6265  * @cfg {String} valign Vertical aligns the content in a cell
6266  * @cfg {Number} width Specifies the width of a cell
6267  * 
6268  * @constructor
6269  * Create a new TableCell
6270  * @param {Object} config The config object
6271  */
6272
6273 Roo.bootstrap.TableCell = function(config){
6274     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6275 };
6276
6277 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6278     
6279     html: false,
6280     cls: false,
6281     tag: false,
6282     abbr: false,
6283     align: false,
6284     axis: false,
6285     bgcolor: false,
6286     charoff: false,
6287     colspan: false,
6288     headers: false,
6289     height: false,
6290     nowrap: false,
6291     rowspan: false,
6292     scope: false,
6293     valign: false,
6294     width: false,
6295     
6296     
6297     getAutoCreate : function(){
6298         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6299         
6300         cfg = {
6301             tag: 'td'
6302         }
6303         
6304         if(this.tag){
6305             cfg.tag = this.tag;
6306         }
6307         
6308         if (this.html) {
6309             cfg.html=this.html
6310         }
6311         if (this.cls) {
6312             cfg.cls=this.cls
6313         }
6314         if (this.abbr) {
6315             cfg.abbr=this.abbr
6316         }
6317         if (this.align) {
6318             cfg.align=this.align
6319         }
6320         if (this.axis) {
6321             cfg.axis=this.axis
6322         }
6323         if (this.bgcolor) {
6324             cfg.bgcolor=this.bgcolor
6325         }
6326         if (this.charoff) {
6327             cfg.charoff=this.charoff
6328         }
6329         if (this.colspan) {
6330             cfg.colspan=this.colspan
6331         }
6332         if (this.headers) {
6333             cfg.headers=this.headers
6334         }
6335         if (this.height) {
6336             cfg.height=this.height
6337         }
6338         if (this.nowrap) {
6339             cfg.nowrap=this.nowrap
6340         }
6341         if (this.rowspan) {
6342             cfg.rowspan=this.rowspan
6343         }
6344         if (this.scope) {
6345             cfg.scope=this.scope
6346         }
6347         if (this.valign) {
6348             cfg.valign=this.valign
6349         }
6350         if (this.width) {
6351             cfg.width=this.width
6352         }
6353         
6354         
6355         return cfg;
6356     }
6357    
6358 });
6359
6360  
6361
6362  /*
6363  * - LGPL
6364  *
6365  * table row
6366  * 
6367  */
6368
6369 /**
6370  * @class Roo.bootstrap.TableRow
6371  * @extends Roo.bootstrap.Component
6372  * Bootstrap TableRow class
6373  * @cfg {String} cls row class
6374  * @cfg {String} align Aligns the content in a table row
6375  * @cfg {String} bgcolor Specifies a background color for a table row
6376  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6377  * @cfg {String} valign Vertical aligns the content in a table row
6378  * 
6379  * @constructor
6380  * Create a new TableRow
6381  * @param {Object} config The config object
6382  */
6383
6384 Roo.bootstrap.TableRow = function(config){
6385     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6386 };
6387
6388 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6389     
6390     cls: false,
6391     align: false,
6392     bgcolor: false,
6393     charoff: false,
6394     valign: false,
6395     
6396     getAutoCreate : function(){
6397         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6398         
6399         cfg = {
6400             tag: 'tr'
6401         }
6402             
6403         if(this.cls){
6404             cfg.cls = this.cls;
6405         }
6406         if(this.align){
6407             cfg.align = this.align;
6408         }
6409         if(this.bgcolor){
6410             cfg.bgcolor = this.bgcolor;
6411         }
6412         if(this.charoff){
6413             cfg.charoff = this.charoff;
6414         }
6415         if(this.valign){
6416             cfg.valign = this.valign;
6417         }
6418         
6419         return cfg;
6420     }
6421    
6422 });
6423
6424  
6425
6426  /*
6427  * - LGPL
6428  *
6429  * table body
6430  * 
6431  */
6432
6433 /**
6434  * @class Roo.bootstrap.TableBody
6435  * @extends Roo.bootstrap.Component
6436  * Bootstrap TableBody class
6437  * @cfg {String} cls element class
6438  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6439  * @cfg {String} align Aligns the content inside the element
6440  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6441  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6442  * 
6443  * @constructor
6444  * Create a new TableBody
6445  * @param {Object} config The config object
6446  */
6447
6448 Roo.bootstrap.TableBody = function(config){
6449     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6450 };
6451
6452 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6453     
6454     cls: false,
6455     tag: false,
6456     align: false,
6457     charoff: false,
6458     valign: false,
6459     
6460     getAutoCreate : function(){
6461         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6462         
6463         cfg = {
6464             tag: 'tbody'
6465         }
6466             
6467         if (this.cls) {
6468             cfg.cls=this.cls
6469         }
6470         if(this.tag){
6471             cfg.tag = this.tag;
6472         }
6473         
6474         if(this.align){
6475             cfg.align = this.align;
6476         }
6477         if(this.charoff){
6478             cfg.charoff = this.charoff;
6479         }
6480         if(this.valign){
6481             cfg.valign = this.valign;
6482         }
6483         
6484         return cfg;
6485     }
6486     
6487     
6488 //    initEvents : function()
6489 //    {
6490 //        
6491 //        if(!this.store){
6492 //            return;
6493 //        }
6494 //        
6495 //        this.store = Roo.factory(this.store, Roo.data);
6496 //        this.store.on('load', this.onLoad, this);
6497 //        
6498 //        this.store.load();
6499 //        
6500 //    },
6501 //    
6502 //    onLoad: function () 
6503 //    {   
6504 //        this.fireEvent('load', this);
6505 //    }
6506 //    
6507 //   
6508 });
6509
6510  
6511
6512  /*
6513  * Based on:
6514  * Ext JS Library 1.1.1
6515  * Copyright(c) 2006-2007, Ext JS, LLC.
6516  *
6517  * Originally Released Under LGPL - original licence link has changed is not relivant.
6518  *
6519  * Fork - LGPL
6520  * <script type="text/javascript">
6521  */
6522
6523 // as we use this in bootstrap.
6524 Roo.namespace('Roo.form');
6525  /**
6526  * @class Roo.form.Action
6527  * Internal Class used to handle form actions
6528  * @constructor
6529  * @param {Roo.form.BasicForm} el The form element or its id
6530  * @param {Object} config Configuration options
6531  */
6532
6533  
6534  
6535 // define the action interface
6536 Roo.form.Action = function(form, options){
6537     this.form = form;
6538     this.options = options || {};
6539 };
6540 /**
6541  * Client Validation Failed
6542  * @const 
6543  */
6544 Roo.form.Action.CLIENT_INVALID = 'client';
6545 /**
6546  * Server Validation Failed
6547  * @const 
6548  */
6549 Roo.form.Action.SERVER_INVALID = 'server';
6550  /**
6551  * Connect to Server Failed
6552  * @const 
6553  */
6554 Roo.form.Action.CONNECT_FAILURE = 'connect';
6555 /**
6556  * Reading Data from Server Failed
6557  * @const 
6558  */
6559 Roo.form.Action.LOAD_FAILURE = 'load';
6560
6561 Roo.form.Action.prototype = {
6562     type : 'default',
6563     failureType : undefined,
6564     response : undefined,
6565     result : undefined,
6566
6567     // interface method
6568     run : function(options){
6569
6570     },
6571
6572     // interface method
6573     success : function(response){
6574
6575     },
6576
6577     // interface method
6578     handleResponse : function(response){
6579
6580     },
6581
6582     // default connection failure
6583     failure : function(response){
6584         
6585         this.response = response;
6586         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6587         this.form.afterAction(this, false);
6588     },
6589
6590     processResponse : function(response){
6591         this.response = response;
6592         if(!response.responseText){
6593             return true;
6594         }
6595         this.result = this.handleResponse(response);
6596         return this.result;
6597     },
6598
6599     // utility functions used internally
6600     getUrl : function(appendParams){
6601         var url = this.options.url || this.form.url || this.form.el.dom.action;
6602         if(appendParams){
6603             var p = this.getParams();
6604             if(p){
6605                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6606             }
6607         }
6608         return url;
6609     },
6610
6611     getMethod : function(){
6612         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6613     },
6614
6615     getParams : function(){
6616         var bp = this.form.baseParams;
6617         var p = this.options.params;
6618         if(p){
6619             if(typeof p == "object"){
6620                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6621             }else if(typeof p == 'string' && bp){
6622                 p += '&' + Roo.urlEncode(bp);
6623             }
6624         }else if(bp){
6625             p = Roo.urlEncode(bp);
6626         }
6627         return p;
6628     },
6629
6630     createCallback : function(){
6631         return {
6632             success: this.success,
6633             failure: this.failure,
6634             scope: this,
6635             timeout: (this.form.timeout*1000),
6636             upload: this.form.fileUpload ? this.success : undefined
6637         };
6638     }
6639 };
6640
6641 Roo.form.Action.Submit = function(form, options){
6642     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6643 };
6644
6645 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6646     type : 'submit',
6647
6648     haveProgress : false,
6649     uploadComplete : false,
6650     
6651     // uploadProgress indicator.
6652     uploadProgress : function()
6653     {
6654         if (!this.form.progressUrl) {
6655             return;
6656         }
6657         
6658         if (!this.haveProgress) {
6659             Roo.MessageBox.progress("Uploading", "Uploading");
6660         }
6661         if (this.uploadComplete) {
6662            Roo.MessageBox.hide();
6663            return;
6664         }
6665         
6666         this.haveProgress = true;
6667    
6668         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6669         
6670         var c = new Roo.data.Connection();
6671         c.request({
6672             url : this.form.progressUrl,
6673             params: {
6674                 id : uid
6675             },
6676             method: 'GET',
6677             success : function(req){
6678                //console.log(data);
6679                 var rdata = false;
6680                 var edata;
6681                 try  {
6682                    rdata = Roo.decode(req.responseText)
6683                 } catch (e) {
6684                     Roo.log("Invalid data from server..");
6685                     Roo.log(edata);
6686                     return;
6687                 }
6688                 if (!rdata || !rdata.success) {
6689                     Roo.log(rdata);
6690                     Roo.MessageBox.alert(Roo.encode(rdata));
6691                     return;
6692                 }
6693                 var data = rdata.data;
6694                 
6695                 if (this.uploadComplete) {
6696                    Roo.MessageBox.hide();
6697                    return;
6698                 }
6699                    
6700                 if (data){
6701                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6702                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6703                     );
6704                 }
6705                 this.uploadProgress.defer(2000,this);
6706             },
6707        
6708             failure: function(data) {
6709                 Roo.log('progress url failed ');
6710                 Roo.log(data);
6711             },
6712             scope : this
6713         });
6714            
6715     },
6716     
6717     
6718     run : function()
6719     {
6720         // run get Values on the form, so it syncs any secondary forms.
6721         this.form.getValues();
6722         
6723         var o = this.options;
6724         var method = this.getMethod();
6725         var isPost = method == 'POST';
6726         if(o.clientValidation === false || this.form.isValid()){
6727             
6728             if (this.form.progressUrl) {
6729                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6730                     (new Date() * 1) + '' + Math.random());
6731                     
6732             } 
6733             
6734             
6735             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6736                 form:this.form.el.dom,
6737                 url:this.getUrl(!isPost),
6738                 method: method,
6739                 params:isPost ? this.getParams() : null,
6740                 isUpload: this.form.fileUpload
6741             }));
6742             
6743             this.uploadProgress();
6744
6745         }else if (o.clientValidation !== false){ // client validation failed
6746             this.failureType = Roo.form.Action.CLIENT_INVALID;
6747             this.form.afterAction(this, false);
6748         }
6749     },
6750
6751     success : function(response)
6752     {
6753         this.uploadComplete= true;
6754         if (this.haveProgress) {
6755             Roo.MessageBox.hide();
6756         }
6757         
6758         
6759         var result = this.processResponse(response);
6760         if(result === true || result.success){
6761             this.form.afterAction(this, true);
6762             return;
6763         }
6764         if(result.errors){
6765             this.form.markInvalid(result.errors);
6766             this.failureType = Roo.form.Action.SERVER_INVALID;
6767         }
6768         this.form.afterAction(this, false);
6769     },
6770     failure : function(response)
6771     {
6772         this.uploadComplete= true;
6773         if (this.haveProgress) {
6774             Roo.MessageBox.hide();
6775         }
6776         
6777         this.response = response;
6778         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6779         this.form.afterAction(this, false);
6780     },
6781     
6782     handleResponse : function(response){
6783         if(this.form.errorReader){
6784             var rs = this.form.errorReader.read(response);
6785             var errors = [];
6786             if(rs.records){
6787                 for(var i = 0, len = rs.records.length; i < len; i++) {
6788                     var r = rs.records[i];
6789                     errors[i] = r.data;
6790                 }
6791             }
6792             if(errors.length < 1){
6793                 errors = null;
6794             }
6795             return {
6796                 success : rs.success,
6797                 errors : errors
6798             };
6799         }
6800         var ret = false;
6801         try {
6802             ret = Roo.decode(response.responseText);
6803         } catch (e) {
6804             ret = {
6805                 success: false,
6806                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6807                 errors : []
6808             };
6809         }
6810         return ret;
6811         
6812     }
6813 });
6814
6815
6816 Roo.form.Action.Load = function(form, options){
6817     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6818     this.reader = this.form.reader;
6819 };
6820
6821 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6822     type : 'load',
6823
6824     run : function(){
6825         
6826         Roo.Ajax.request(Roo.apply(
6827                 this.createCallback(), {
6828                     method:this.getMethod(),
6829                     url:this.getUrl(false),
6830                     params:this.getParams()
6831         }));
6832     },
6833
6834     success : function(response){
6835         
6836         var result = this.processResponse(response);
6837         if(result === true || !result.success || !result.data){
6838             this.failureType = Roo.form.Action.LOAD_FAILURE;
6839             this.form.afterAction(this, false);
6840             return;
6841         }
6842         this.form.clearInvalid();
6843         this.form.setValues(result.data);
6844         this.form.afterAction(this, true);
6845     },
6846
6847     handleResponse : function(response){
6848         if(this.form.reader){
6849             var rs = this.form.reader.read(response);
6850             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6851             return {
6852                 success : rs.success,
6853                 data : data
6854             };
6855         }
6856         return Roo.decode(response.responseText);
6857     }
6858 });
6859
6860 Roo.form.Action.ACTION_TYPES = {
6861     'load' : Roo.form.Action.Load,
6862     'submit' : Roo.form.Action.Submit
6863 };/*
6864  * - LGPL
6865  *
6866  * form
6867  * 
6868  */
6869
6870 /**
6871  * @class Roo.bootstrap.Form
6872  * @extends Roo.bootstrap.Component
6873  * Bootstrap Form class
6874  * @cfg {String} method  GET | POST (default POST)
6875  * @cfg {String} labelAlign top | left (default top)
6876  * @cfg {String} align left  | right - for navbars
6877  * @cfg {Boolean} loadMask load mask when submit (default true)
6878
6879  * 
6880  * @constructor
6881  * Create a new Form
6882  * @param {Object} config The config object
6883  */
6884
6885
6886 Roo.bootstrap.Form = function(config){
6887     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6888     this.addEvents({
6889         /**
6890          * @event clientvalidation
6891          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6892          * @param {Form} this
6893          * @param {Boolean} valid true if the form has passed client-side validation
6894          */
6895         clientvalidation: true,
6896         /**
6897          * @event beforeaction
6898          * Fires before any action is performed. Return false to cancel the action.
6899          * @param {Form} this
6900          * @param {Action} action The action to be performed
6901          */
6902         beforeaction: true,
6903         /**
6904          * @event actionfailed
6905          * Fires when an action fails.
6906          * @param {Form} this
6907          * @param {Action} action The action that failed
6908          */
6909         actionfailed : true,
6910         /**
6911          * @event actioncomplete
6912          * Fires when an action is completed.
6913          * @param {Form} this
6914          * @param {Action} action The action that completed
6915          */
6916         actioncomplete : true
6917     });
6918     
6919 };
6920
6921 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6922       
6923      /**
6924      * @cfg {String} method
6925      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6926      */
6927     method : 'POST',
6928     /**
6929      * @cfg {String} url
6930      * The URL to use for form actions if one isn't supplied in the action options.
6931      */
6932     /**
6933      * @cfg {Boolean} fileUpload
6934      * Set to true if this form is a file upload.
6935      */
6936      
6937     /**
6938      * @cfg {Object} baseParams
6939      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6940      */
6941       
6942     /**
6943      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6944      */
6945     timeout: 30,
6946     /**
6947      * @cfg {Sting} align (left|right) for navbar forms
6948      */
6949     align : 'left',
6950
6951     // private
6952     activeAction : null,
6953  
6954     /**
6955      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6956      * element by passing it or its id or mask the form itself by passing in true.
6957      * @type Mixed
6958      */
6959     waitMsgTarget : false,
6960     
6961     loadMask : true,
6962     
6963     getAutoCreate : function(){
6964         
6965         var cfg = {
6966             tag: 'form',
6967             method : this.method || 'POST',
6968             id : this.id || Roo.id(),
6969             cls : ''
6970         }
6971         if (this.parent().xtype.match(/^Nav/)) {
6972             cfg.cls = 'navbar-form navbar-' + this.align;
6973             
6974         }
6975         
6976         if (this.labelAlign == 'left' ) {
6977             cfg.cls += ' form-horizontal';
6978         }
6979         
6980         
6981         return cfg;
6982     },
6983     initEvents : function()
6984     {
6985         this.el.on('submit', this.onSubmit, this);
6986         // this was added as random key presses on the form where triggering form submit.
6987         this.el.on('keypress', function(e) {
6988             if (e.getCharCode() != 13) {
6989                 return true;
6990             }
6991             // we might need to allow it for textareas.. and some other items.
6992             // check e.getTarget().
6993             
6994             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6995                 return true;
6996             }
6997         
6998             Roo.log("keypress blocked");
6999             
7000             e.preventDefault();
7001             return false;
7002         });
7003         
7004     },
7005     // private
7006     onSubmit : function(e){
7007         e.stopEvent();
7008     },
7009     
7010      /**
7011      * Returns true if client-side validation on the form is successful.
7012      * @return Boolean
7013      */
7014     isValid : function(){
7015         var items = this.getItems();
7016         var valid = true;
7017         items.each(function(f){
7018            if(!f.validate()){
7019                valid = false;
7020                
7021            }
7022         });
7023         return valid;
7024     },
7025     /**
7026      * Returns true if any fields in this form have changed since their original load.
7027      * @return Boolean
7028      */
7029     isDirty : function(){
7030         var dirty = false;
7031         var items = this.getItems();
7032         items.each(function(f){
7033            if(f.isDirty()){
7034                dirty = true;
7035                return false;
7036            }
7037            return true;
7038         });
7039         return dirty;
7040     },
7041      /**
7042      * Performs a predefined action (submit or load) or custom actions you define on this form.
7043      * @param {String} actionName The name of the action type
7044      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7045      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7046      * accept other config options):
7047      * <pre>
7048 Property          Type             Description
7049 ----------------  ---------------  ----------------------------------------------------------------------------------
7050 url               String           The url for the action (defaults to the form's url)
7051 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7052 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7053 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7054                                    validate the form on the client (defaults to false)
7055      * </pre>
7056      * @return {BasicForm} this
7057      */
7058     doAction : function(action, options){
7059         if(typeof action == 'string'){
7060             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7061         }
7062         if(this.fireEvent('beforeaction', this, action) !== false){
7063             this.beforeAction(action);
7064             action.run.defer(100, action);
7065         }
7066         return this;
7067     },
7068     
7069     // private
7070     beforeAction : function(action){
7071         var o = action.options;
7072         
7073         if(this.loadMask){
7074             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7075         }
7076         // not really supported yet.. ??
7077         
7078         //if(this.waitMsgTarget === true){
7079         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7080         //}else if(this.waitMsgTarget){
7081         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7082         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7083         //}else {
7084         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7085        // }
7086          
7087     },
7088
7089     // private
7090     afterAction : function(action, success){
7091         this.activeAction = null;
7092         var o = action.options;
7093         
7094         //if(this.waitMsgTarget === true){
7095             this.el.unmask();
7096         //}else if(this.waitMsgTarget){
7097         //    this.waitMsgTarget.unmask();
7098         //}else{
7099         //    Roo.MessageBox.updateProgress(1);
7100         //    Roo.MessageBox.hide();
7101        // }
7102         // 
7103         if(success){
7104             if(o.reset){
7105                 this.reset();
7106             }
7107             Roo.callback(o.success, o.scope, [this, action]);
7108             this.fireEvent('actioncomplete', this, action);
7109             
7110         }else{
7111             
7112             // failure condition..
7113             // we have a scenario where updates need confirming.
7114             // eg. if a locking scenario exists..
7115             // we look for { errors : { needs_confirm : true }} in the response.
7116             if (
7117                 (typeof(action.result) != 'undefined')  &&
7118                 (typeof(action.result.errors) != 'undefined')  &&
7119                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7120            ){
7121                 var _t = this;
7122                 Roo.log("not supported yet");
7123                  /*
7124                 
7125                 Roo.MessageBox.confirm(
7126                     "Change requires confirmation",
7127                     action.result.errorMsg,
7128                     function(r) {
7129                         if (r != 'yes') {
7130                             return;
7131                         }
7132                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7133                     }
7134                     
7135                 );
7136                 */
7137                 
7138                 
7139                 return;
7140             }
7141             
7142             Roo.callback(o.failure, o.scope, [this, action]);
7143             // show an error message if no failed handler is set..
7144             if (!this.hasListener('actionfailed')) {
7145                 Roo.log("need to add dialog support");
7146                 /*
7147                 Roo.MessageBox.alert("Error",
7148                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7149                         action.result.errorMsg :
7150                         "Saving Failed, please check your entries or try again"
7151                 );
7152                 */
7153             }
7154             
7155             this.fireEvent('actionfailed', this, action);
7156         }
7157         
7158     },
7159     /**
7160      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7161      * @param {String} id The value to search for
7162      * @return Field
7163      */
7164     findField : function(id){
7165         var items = this.getItems();
7166         var field = items.get(id);
7167         if(!field){
7168              items.each(function(f){
7169                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7170                     field = f;
7171                     return false;
7172                 }
7173                 return true;
7174             });
7175         }
7176         return field || null;
7177     },
7178      /**
7179      * Mark fields in this form invalid in bulk.
7180      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7181      * @return {BasicForm} this
7182      */
7183     markInvalid : function(errors){
7184         if(errors instanceof Array){
7185             for(var i = 0, len = errors.length; i < len; i++){
7186                 var fieldError = errors[i];
7187                 var f = this.findField(fieldError.id);
7188                 if(f){
7189                     f.markInvalid(fieldError.msg);
7190                 }
7191             }
7192         }else{
7193             var field, id;
7194             for(id in errors){
7195                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7196                     field.markInvalid(errors[id]);
7197                 }
7198             }
7199         }
7200         //Roo.each(this.childForms || [], function (f) {
7201         //    f.markInvalid(errors);
7202         //});
7203         
7204         return this;
7205     },
7206
7207     /**
7208      * Set values for fields in this form in bulk.
7209      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7210      * @return {BasicForm} this
7211      */
7212     setValues : function(values){
7213         if(values instanceof Array){ // array of objects
7214             for(var i = 0, len = values.length; i < len; i++){
7215                 var v = values[i];
7216                 var f = this.findField(v.id);
7217                 if(f){
7218                     f.setValue(v.value);
7219                     if(this.trackResetOnLoad){
7220                         f.originalValue = f.getValue();
7221                     }
7222                 }
7223             }
7224         }else{ // object hash
7225             var field, id;
7226             for(id in values){
7227                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7228                     
7229                     if (field.setFromData && 
7230                         field.valueField && 
7231                         field.displayField &&
7232                         // combos' with local stores can 
7233                         // be queried via setValue()
7234                         // to set their value..
7235                         (field.store && !field.store.isLocal)
7236                         ) {
7237                         // it's a combo
7238                         var sd = { };
7239                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7240                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7241                         field.setFromData(sd);
7242                         
7243                     } else {
7244                         field.setValue(values[id]);
7245                     }
7246                     
7247                     
7248                     if(this.trackResetOnLoad){
7249                         field.originalValue = field.getValue();
7250                     }
7251                 }
7252             }
7253         }
7254          
7255         //Roo.each(this.childForms || [], function (f) {
7256         //    f.setValues(values);
7257         //});
7258                 
7259         return this;
7260     },
7261
7262     /**
7263      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7264      * they are returned as an array.
7265      * @param {Boolean} asString
7266      * @return {Object}
7267      */
7268     getValues : function(asString){
7269         //if (this.childForms) {
7270             // copy values from the child forms
7271         //    Roo.each(this.childForms, function (f) {
7272         //        this.setValues(f.getValues());
7273         //    }, this);
7274         //}
7275         
7276         
7277         
7278         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7279         if(asString === true){
7280             return fs;
7281         }
7282         return Roo.urlDecode(fs);
7283     },
7284     
7285     /**
7286      * Returns the fields in this form as an object with key/value pairs. 
7287      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7288      * @return {Object}
7289      */
7290     getFieldValues : function(with_hidden)
7291     {
7292         var items = this.getItems();
7293         var ret = {};
7294         items.each(function(f){
7295             if (!f.getName()) {
7296                 return;
7297             }
7298             var v = f.getValue();
7299             if (f.inputType =='radio') {
7300                 if (typeof(ret[f.getName()]) == 'undefined') {
7301                     ret[f.getName()] = ''; // empty..
7302                 }
7303                 
7304                 if (!f.el.dom.checked) {
7305                     return;
7306                     
7307                 }
7308                 v = f.el.dom.value;
7309                 
7310             }
7311             
7312             // not sure if this supported any more..
7313             if ((typeof(v) == 'object') && f.getRawValue) {
7314                 v = f.getRawValue() ; // dates..
7315             }
7316             // combo boxes where name != hiddenName...
7317             if (f.name != f.getName()) {
7318                 ret[f.name] = f.getRawValue();
7319             }
7320             ret[f.getName()] = v;
7321         });
7322         
7323         return ret;
7324     },
7325
7326     /**
7327      * Clears all invalid messages in this form.
7328      * @return {BasicForm} this
7329      */
7330     clearInvalid : function(){
7331         var items = this.getItems();
7332         
7333         items.each(function(f){
7334            f.clearInvalid();
7335         });
7336         
7337         
7338         
7339         return this;
7340     },
7341
7342     /**
7343      * Resets this form.
7344      * @return {BasicForm} this
7345      */
7346     reset : function(){
7347         var items = this.getItems();
7348         items.each(function(f){
7349             f.reset();
7350         });
7351         
7352         Roo.each(this.childForms || [], function (f) {
7353             f.reset();
7354         });
7355        
7356         
7357         return this;
7358     },
7359     getItems : function()
7360     {
7361         var r=new Roo.util.MixedCollection(false, function(o){
7362             return o.id || (o.id = Roo.id());
7363         });
7364         var iter = function(el) {
7365             if (el.inputEl) {
7366                 r.add(el);
7367             }
7368             if (!el.items) {
7369                 return;
7370             }
7371             Roo.each(el.items,function(e) {
7372                 iter(e);
7373             });
7374             
7375             
7376         };
7377         
7378         iter(this);
7379         return r;
7380         
7381         
7382         
7383         
7384     }
7385     
7386 });
7387
7388  
7389 /*
7390  * Based on:
7391  * Ext JS Library 1.1.1
7392  * Copyright(c) 2006-2007, Ext JS, LLC.
7393  *
7394  * Originally Released Under LGPL - original licence link has changed is not relivant.
7395  *
7396  * Fork - LGPL
7397  * <script type="text/javascript">
7398  */
7399 /**
7400  * @class Roo.form.VTypes
7401  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7402  * @singleton
7403  */
7404 Roo.form.VTypes = function(){
7405     // closure these in so they are only created once.
7406     var alpha = /^[a-zA-Z_]+$/;
7407     var alphanum = /^[a-zA-Z0-9_]+$/;
7408     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7409     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7410
7411     // All these messages and functions are configurable
7412     return {
7413         /**
7414          * The function used to validate email addresses
7415          * @param {String} value The email address
7416          */
7417         'email' : function(v){
7418             return email.test(v);
7419         },
7420         /**
7421          * The error text to display when the email validation function returns false
7422          * @type String
7423          */
7424         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7425         /**
7426          * The keystroke filter mask to be applied on email input
7427          * @type RegExp
7428          */
7429         'emailMask' : /[a-z0-9_\.\-@]/i,
7430
7431         /**
7432          * The function used to validate URLs
7433          * @param {String} value The URL
7434          */
7435         'url' : function(v){
7436             return url.test(v);
7437         },
7438         /**
7439          * The error text to display when the url validation function returns false
7440          * @type String
7441          */
7442         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7443         
7444         /**
7445          * The function used to validate alpha values
7446          * @param {String} value The value
7447          */
7448         'alpha' : function(v){
7449             return alpha.test(v);
7450         },
7451         /**
7452          * The error text to display when the alpha validation function returns false
7453          * @type String
7454          */
7455         'alphaText' : 'This field should only contain letters and _',
7456         /**
7457          * The keystroke filter mask to be applied on alpha input
7458          * @type RegExp
7459          */
7460         'alphaMask' : /[a-z_]/i,
7461
7462         /**
7463          * The function used to validate alphanumeric values
7464          * @param {String} value The value
7465          */
7466         'alphanum' : function(v){
7467             return alphanum.test(v);
7468         },
7469         /**
7470          * The error text to display when the alphanumeric validation function returns false
7471          * @type String
7472          */
7473         'alphanumText' : 'This field should only contain letters, numbers and _',
7474         /**
7475          * The keystroke filter mask to be applied on alphanumeric input
7476          * @type RegExp
7477          */
7478         'alphanumMask' : /[a-z0-9_]/i
7479     };
7480 }();/*
7481  * - LGPL
7482  *
7483  * Input
7484  * 
7485  */
7486
7487 /**
7488  * @class Roo.bootstrap.Input
7489  * @extends Roo.bootstrap.Component
7490  * Bootstrap Input class
7491  * @cfg {Boolean} disabled is it disabled
7492  * @cfg {String} fieldLabel - the label associated
7493  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7494  * @cfg {String} name name of the input
7495  * @cfg {string} fieldLabel - the label associated
7496  * @cfg {string}  inputType - input / file submit ...
7497  * @cfg {string} placeholder - placeholder to put in text.
7498  * @cfg {string}  before - input group add on before
7499  * @cfg {string} after - input group add on after
7500  * @cfg {string} size - (lg|sm) or leave empty..
7501  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7502  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7503  * @cfg {Number} md colspan out of 12 for computer-sized screens
7504  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7505  * @cfg {string} value default value of the input
7506  * @cfg {Number} labelWidth set the width of label (0-12)
7507  * @cfg {String} labelAlign (top|left)
7508  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7509  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7510
7511  * @cfg {String} align (left|center|right) Default left
7512  * @cfg {Boolean} forceFeedback (true|false) Default false
7513  * 
7514  * 
7515  * 
7516  * 
7517  * @constructor
7518  * Create a new Input
7519  * @param {Object} config The config object
7520  */
7521
7522 Roo.bootstrap.Input = function(config){
7523     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7524    
7525         this.addEvents({
7526             /**
7527              * @event focus
7528              * Fires when this field receives input focus.
7529              * @param {Roo.form.Field} this
7530              */
7531             focus : true,
7532             /**
7533              * @event blur
7534              * Fires when this field loses input focus.
7535              * @param {Roo.form.Field} this
7536              */
7537             blur : true,
7538             /**
7539              * @event specialkey
7540              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7541              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7542              * @param {Roo.form.Field} this
7543              * @param {Roo.EventObject} e The event object
7544              */
7545             specialkey : true,
7546             /**
7547              * @event change
7548              * Fires just before the field blurs if the field value has changed.
7549              * @param {Roo.form.Field} this
7550              * @param {Mixed} newValue The new value
7551              * @param {Mixed} oldValue The original value
7552              */
7553             change : true,
7554             /**
7555              * @event invalid
7556              * Fires after the field has been marked as invalid.
7557              * @param {Roo.form.Field} this
7558              * @param {String} msg The validation message
7559              */
7560             invalid : true,
7561             /**
7562              * @event valid
7563              * Fires after the field has been validated with no errors.
7564              * @param {Roo.form.Field} this
7565              */
7566             valid : true,
7567              /**
7568              * @event keyup
7569              * Fires after the key up
7570              * @param {Roo.form.Field} this
7571              * @param {Roo.EventObject}  e The event Object
7572              */
7573             keyup : true
7574         });
7575 };
7576
7577 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7578      /**
7579      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7580       automatic validation (defaults to "keyup").
7581      */
7582     validationEvent : "keyup",
7583      /**
7584      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7585      */
7586     validateOnBlur : true,
7587     /**
7588      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7589      */
7590     validationDelay : 250,
7591      /**
7592      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7593      */
7594     focusClass : "x-form-focus",  // not needed???
7595     
7596        
7597     /**
7598      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7599      */
7600     invalidClass : "has-warning",
7601     
7602     /**
7603      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7604      */
7605     validClass : "has-success",
7606     
7607     /**
7608      * @cfg {Boolean} hasFeedback (true|false) default true
7609      */
7610     hasFeedback : true,
7611     
7612     /**
7613      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7614      */
7615     invalidFeedbackClass : "glyphicon-warning-sign",
7616     
7617     /**
7618      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7619      */
7620     validFeedbackClass : "glyphicon-ok",
7621     
7622     /**
7623      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7624      */
7625     selectOnFocus : false,
7626     
7627      /**
7628      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7629      */
7630     maskRe : null,
7631        /**
7632      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7633      */
7634     vtype : null,
7635     
7636       /**
7637      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7638      */
7639     disableKeyFilter : false,
7640     
7641        /**
7642      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7643      */
7644     disabled : false,
7645      /**
7646      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7647      */
7648     allowBlank : true,
7649     /**
7650      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7651      */
7652     blankText : "This field is required",
7653     
7654      /**
7655      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7656      */
7657     minLength : 0,
7658     /**
7659      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7660      */
7661     maxLength : Number.MAX_VALUE,
7662     /**
7663      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7664      */
7665     minLengthText : "The minimum length for this field is {0}",
7666     /**
7667      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7668      */
7669     maxLengthText : "The maximum length for this field is {0}",
7670   
7671     
7672     /**
7673      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7674      * If available, this function will be called only after the basic validators all return true, and will be passed the
7675      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7676      */
7677     validator : null,
7678     /**
7679      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7680      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7681      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7682      */
7683     regex : null,
7684     /**
7685      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7686      */
7687     regexText : "",
7688     
7689     autocomplete: false,
7690     
7691     
7692     fieldLabel : '',
7693     inputType : 'text',
7694     
7695     name : false,
7696     placeholder: false,
7697     before : false,
7698     after : false,
7699     size : false,
7700     hasFocus : false,
7701     preventMark: false,
7702     isFormField : true,
7703     value : '',
7704     labelWidth : 2,
7705     labelAlign : false,
7706     readOnly : false,
7707     align : false,
7708     formatedValue : false,
7709     forceFeedback : false,
7710     
7711     parentLabelAlign : function()
7712     {
7713         var parent = this;
7714         while (parent.parent()) {
7715             parent = parent.parent();
7716             if (typeof(parent.labelAlign) !='undefined') {
7717                 return parent.labelAlign;
7718             }
7719         }
7720         return 'left';
7721         
7722     },
7723     
7724     getAutoCreate : function(){
7725         
7726         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7727         
7728         var id = Roo.id();
7729         
7730         var cfg = {};
7731         
7732         if(this.inputType != 'hidden'){
7733             cfg.cls = 'form-group' //input-group
7734         }
7735         
7736         var input =  {
7737             tag: 'input',
7738             id : id,
7739             type : this.inputType,
7740             value : this.value,
7741             cls : 'form-control',
7742             placeholder : this.placeholder || '',
7743             autocomplete : this.autocomplete || 'new-password'
7744         };
7745         
7746         
7747         if(this.align){
7748             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7749         }
7750         
7751         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7752             input.maxLength = this.maxLength;
7753         }
7754         
7755         if (this.disabled) {
7756             input.disabled=true;
7757         }
7758         
7759         if (this.readOnly) {
7760             input.readonly=true;
7761         }
7762         
7763         if (this.name) {
7764             input.name = this.name;
7765         }
7766         if (this.size) {
7767             input.cls += ' input-' + this.size;
7768         }
7769         var settings=this;
7770         ['xs','sm','md','lg'].map(function(size){
7771             if (settings[size]) {
7772                 cfg.cls += ' col-' + size + '-' + settings[size];
7773             }
7774         });
7775         
7776         var inputblock = input;
7777         
7778         var feedback = {
7779             tag: 'span',
7780             cls: 'glyphicon form-control-feedback'
7781         };
7782             
7783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7784             
7785             inputblock = {
7786                 cls : 'has-feedback',
7787                 cn :  [
7788                     input,
7789                     feedback
7790                 ] 
7791             };  
7792         }
7793         
7794         if (this.before || this.after) {
7795             
7796             inputblock = {
7797                 cls : 'input-group',
7798                 cn :  [] 
7799             };
7800             
7801             if (this.before && typeof(this.before) == 'string') {
7802                 
7803                 inputblock.cn.push({
7804                     tag :'span',
7805                     cls : 'roo-input-before input-group-addon',
7806                     html : this.before
7807                 });
7808             }
7809             if (this.before && typeof(this.before) == 'object') {
7810                 this.before = Roo.factory(this.before);
7811                 Roo.log(this.before);
7812                 inputblock.cn.push({
7813                     tag :'span',
7814                     cls : 'roo-input-before input-group-' +
7815                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7816                 });
7817             }
7818             
7819             inputblock.cn.push(input);
7820             
7821             if (this.after && typeof(this.after) == 'string') {
7822                 inputblock.cn.push({
7823                     tag :'span',
7824                     cls : 'roo-input-after input-group-addon',
7825                     html : this.after
7826                 });
7827             }
7828             if (this.after && typeof(this.after) == 'object') {
7829                 this.after = Roo.factory(this.after);
7830                 Roo.log(this.after);
7831                 inputblock.cn.push({
7832                     tag :'span',
7833                     cls : 'roo-input-after input-group-' +
7834                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7835                 });
7836             }
7837             
7838             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7839                 inputblock.cls += ' has-feedback';
7840                 inputblock.cn.push(feedback);
7841             }
7842         };
7843         
7844         if (align ==='left' && this.fieldLabel.length) {
7845                 Roo.log("left and has label");
7846                 cfg.cn = [
7847                     
7848                     {
7849                         tag: 'label',
7850                         'for' :  id,
7851                         cls : 'control-label col-sm-' + this.labelWidth,
7852                         html : this.fieldLabel
7853                         
7854                     },
7855                     {
7856                         cls : "col-sm-" + (12 - this.labelWidth), 
7857                         cn: [
7858                             inputblock
7859                         ]
7860                     }
7861                     
7862                 ];
7863         } else if ( this.fieldLabel.length) {
7864                 Roo.log(" label");
7865                  cfg.cn = [
7866                    
7867                     {
7868                         tag: 'label',
7869                         //cls : 'input-group-addon',
7870                         html : this.fieldLabel
7871                         
7872                     },
7873                     
7874                     inputblock
7875                     
7876                 ];
7877
7878         } else {
7879             
7880                 Roo.log(" no label && no align");
7881                 cfg.cn = [
7882                     
7883                         inputblock
7884                     
7885                 ];
7886                 
7887                 
7888         };
7889         Roo.log('input-parentType: ' + this.parentType);
7890         
7891         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7892            cfg.cls += ' navbar-form';
7893            Roo.log(cfg);
7894         }
7895         
7896         return cfg;
7897         
7898     },
7899     /**
7900      * return the real input element.
7901      */
7902     inputEl: function ()
7903     {
7904         return this.el.select('input.form-control',true).first();
7905     },
7906     
7907     tooltipEl : function()
7908     {
7909         return this.inputEl();
7910     },
7911     
7912     setDisabled : function(v)
7913     {
7914         var i  = this.inputEl().dom;
7915         if (!v) {
7916             i.removeAttribute('disabled');
7917             return;
7918             
7919         }
7920         i.setAttribute('disabled','true');
7921     },
7922     initEvents : function()
7923     {
7924           
7925         this.inputEl().on("keydown" , this.fireKey,  this);
7926         this.inputEl().on("focus", this.onFocus,  this);
7927         this.inputEl().on("blur", this.onBlur,  this);
7928         
7929         this.inputEl().relayEvent('keyup', this);
7930  
7931         // reference to original value for reset
7932         this.originalValue = this.getValue();
7933         //Roo.form.TextField.superclass.initEvents.call(this);
7934         if(this.validationEvent == 'keyup'){
7935             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7936             this.inputEl().on('keyup', this.filterValidation, this);
7937         }
7938         else if(this.validationEvent !== false){
7939             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7940         }
7941         
7942         if(this.selectOnFocus){
7943             this.on("focus", this.preFocus, this);
7944             
7945         }
7946         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7947             this.inputEl().on("keypress", this.filterKeys, this);
7948         }
7949        /* if(this.grow){
7950             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7951             this.el.on("click", this.autoSize,  this);
7952         }
7953         */
7954         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7955             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7956         }
7957         
7958         if (typeof(this.before) == 'object') {
7959             this.before.render(this.el.select('.roo-input-before',true).first());
7960         }
7961         if (typeof(this.after) == 'object') {
7962             this.after.render(this.el.select('.roo-input-after',true).first());
7963         }
7964         
7965         
7966     },
7967     filterValidation : function(e){
7968         if(!e.isNavKeyPress()){
7969             this.validationTask.delay(this.validationDelay);
7970         }
7971     },
7972      /**
7973      * Validates the field value
7974      * @return {Boolean} True if the value is valid, else false
7975      */
7976     validate : function(){
7977         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7978         if(this.disabled || this.validateValue(this.getRawValue())){
7979             this.markValid();
7980             return true;
7981         }
7982         
7983         this.markInvalid();
7984         return false;
7985     },
7986     
7987     
7988     /**
7989      * Validates a value according to the field's validation rules and marks the field as invalid
7990      * if the validation fails
7991      * @param {Mixed} value The value to validate
7992      * @return {Boolean} True if the value is valid, else false
7993      */
7994     validateValue : function(value){
7995         if(value.length < 1)  { // if it's blank
7996             if(this.allowBlank){
7997                 return true;
7998             }
7999             return false;
8000         }
8001         
8002         if(value.length < this.minLength){
8003             return false;
8004         }
8005         if(value.length > this.maxLength){
8006             return false;
8007         }
8008         if(this.vtype){
8009             var vt = Roo.form.VTypes;
8010             if(!vt[this.vtype](value, this)){
8011                 return false;
8012             }
8013         }
8014         if(typeof this.validator == "function"){
8015             var msg = this.validator(value);
8016             if(msg !== true){
8017                 return false;
8018             }
8019         }
8020         
8021         if(this.regex && !this.regex.test(value)){
8022             return false;
8023         }
8024         
8025         return true;
8026     },
8027
8028     
8029     
8030      // private
8031     fireKey : function(e){
8032         //Roo.log('field ' + e.getKey());
8033         if(e.isNavKeyPress()){
8034             this.fireEvent("specialkey", this, e);
8035         }
8036     },
8037     focus : function (selectText){
8038         if(this.rendered){
8039             this.inputEl().focus();
8040             if(selectText === true){
8041                 this.inputEl().dom.select();
8042             }
8043         }
8044         return this;
8045     } ,
8046     
8047     onFocus : function(){
8048         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8049            // this.el.addClass(this.focusClass);
8050         }
8051         if(!this.hasFocus){
8052             this.hasFocus = true;
8053             this.startValue = this.getValue();
8054             this.fireEvent("focus", this);
8055         }
8056     },
8057     
8058     beforeBlur : Roo.emptyFn,
8059
8060     
8061     // private
8062     onBlur : function(){
8063         this.beforeBlur();
8064         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8065             //this.el.removeClass(this.focusClass);
8066         }
8067         this.hasFocus = false;
8068         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8069             this.validate();
8070         }
8071         var v = this.getValue();
8072         if(String(v) !== String(this.startValue)){
8073             this.fireEvent('change', this, v, this.startValue);
8074         }
8075         this.fireEvent("blur", this);
8076     },
8077     
8078     /**
8079      * Resets the current field value to the originally loaded value and clears any validation messages
8080      */
8081     reset : function(){
8082         this.setValue(this.originalValue);
8083         this.validate();
8084     },
8085      /**
8086      * Returns the name of the field
8087      * @return {Mixed} name The name field
8088      */
8089     getName: function(){
8090         return this.name;
8091     },
8092      /**
8093      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8094      * @return {Mixed} value The field value
8095      */
8096     getValue : function(){
8097         
8098         var v = this.inputEl().getValue();
8099         
8100         return v;
8101     },
8102     /**
8103      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8104      * @return {Mixed} value The field value
8105      */
8106     getRawValue : function(){
8107         var v = this.inputEl().getValue();
8108         
8109         return v;
8110     },
8111     
8112     /**
8113      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8114      * @param {Mixed} value The value to set
8115      */
8116     setRawValue : function(v){
8117         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8118     },
8119     
8120     selectText : function(start, end){
8121         var v = this.getRawValue();
8122         if(v.length > 0){
8123             start = start === undefined ? 0 : start;
8124             end = end === undefined ? v.length : end;
8125             var d = this.inputEl().dom;
8126             if(d.setSelectionRange){
8127                 d.setSelectionRange(start, end);
8128             }else if(d.createTextRange){
8129                 var range = d.createTextRange();
8130                 range.moveStart("character", start);
8131                 range.moveEnd("character", v.length-end);
8132                 range.select();
8133             }
8134         }
8135     },
8136     
8137     /**
8138      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8139      * @param {Mixed} value The value to set
8140      */
8141     setValue : function(v){
8142         this.value = v;
8143         if(this.rendered){
8144             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8145             this.validate();
8146         }
8147     },
8148     
8149     /*
8150     processValue : function(value){
8151         if(this.stripCharsRe){
8152             var newValue = value.replace(this.stripCharsRe, '');
8153             if(newValue !== value){
8154                 this.setRawValue(newValue);
8155                 return newValue;
8156             }
8157         }
8158         return value;
8159     },
8160   */
8161     preFocus : function(){
8162         
8163         if(this.selectOnFocus){
8164             this.inputEl().dom.select();
8165         }
8166     },
8167     filterKeys : function(e){
8168         var k = e.getKey();
8169         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8170             return;
8171         }
8172         var c = e.getCharCode(), cc = String.fromCharCode(c);
8173         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8174             return;
8175         }
8176         if(!this.maskRe.test(cc)){
8177             e.stopEvent();
8178         }
8179     },
8180      /**
8181      * Clear any invalid styles/messages for this field
8182      */
8183     clearInvalid : function(){
8184         
8185         if(!this.el || this.preventMark){ // not rendered
8186             return;
8187         }
8188         this.el.removeClass(this.invalidClass);
8189         
8190         this.fireEvent('valid', this);
8191     },
8192     
8193      /**
8194      * Mark this field as valid
8195      */
8196     markValid : function(){
8197         if(!this.el  || this.preventMark){ // not rendered
8198             return;
8199         }
8200         
8201         this.el.removeClass([this.invalidClass, this.validClass]);
8202         
8203         var feedback = this.el.select('.form-control-feedback', true).first();
8204             
8205         if(feedback){
8206             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8207         }
8208
8209         if(this.disabled || this.allowBlank){
8210             return;
8211         }
8212         
8213         this.el.addClass(this.validClass);
8214         
8215         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8216             
8217             var feedback = this.el.select('.form-control-feedback', true).first();
8218             
8219             if(feedback){
8220                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8221                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8222             }
8223             
8224         }
8225         
8226         this.fireEvent('valid', this);
8227     },
8228     
8229      /**
8230      * Mark this field as invalid
8231      * @param {String} msg The validation message
8232      */
8233     markInvalid : function(msg){
8234         if(!this.el  || this.preventMark){ // not rendered
8235             return;
8236         }
8237         
8238         this.el.removeClass([this.invalidClass, this.validClass]);
8239         
8240         var feedback = this.el.select('.form-control-feedback', true).first();
8241             
8242         if(feedback){
8243             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8244         }
8245
8246         if(this.disabled || this.allowBlank){
8247             return;
8248         }
8249         
8250         this.el.addClass(this.invalidClass);
8251         
8252         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8253             
8254             var feedback = this.el.select('.form-control-feedback', true).first();
8255             
8256             if(feedback){
8257                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8258                 
8259                 if(this.getValue().length || this.forceFeedback){
8260                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8261                 }
8262                 
8263             }
8264             
8265         }
8266         
8267         this.fireEvent('invalid', this, msg);
8268     },
8269     // private
8270     SafariOnKeyDown : function(event)
8271     {
8272         // this is a workaround for a password hang bug on chrome/ webkit.
8273         
8274         var isSelectAll = false;
8275         
8276         if(this.inputEl().dom.selectionEnd > 0){
8277             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8278         }
8279         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8280             event.preventDefault();
8281             this.setValue('');
8282             return;
8283         }
8284         
8285         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8286             
8287             event.preventDefault();
8288             // this is very hacky as keydown always get's upper case.
8289             //
8290             var cc = String.fromCharCode(event.getCharCode());
8291             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8292             
8293         }
8294     },
8295     adjustWidth : function(tag, w){
8296         tag = tag.toLowerCase();
8297         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8298             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8299                 if(tag == 'input'){
8300                     return w + 2;
8301                 }
8302                 if(tag == 'textarea'){
8303                     return w-2;
8304                 }
8305             }else if(Roo.isOpera){
8306                 if(tag == 'input'){
8307                     return w + 2;
8308                 }
8309                 if(tag == 'textarea'){
8310                     return w-2;
8311                 }
8312             }
8313         }
8314         return w;
8315     }
8316     
8317 });
8318
8319  
8320 /*
8321  * - LGPL
8322  *
8323  * Input
8324  * 
8325  */
8326
8327 /**
8328  * @class Roo.bootstrap.TextArea
8329  * @extends Roo.bootstrap.Input
8330  * Bootstrap TextArea class
8331  * @cfg {Number} cols Specifies the visible width of a text area
8332  * @cfg {Number} rows Specifies the visible number of lines in a text area
8333  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8334  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8335  * @cfg {string} html text
8336  * 
8337  * @constructor
8338  * Create a new TextArea
8339  * @param {Object} config The config object
8340  */
8341
8342 Roo.bootstrap.TextArea = function(config){
8343     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8344    
8345 };
8346
8347 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8348      
8349     cols : false,
8350     rows : 5,
8351     readOnly : false,
8352     warp : 'soft',
8353     resize : false,
8354     value: false,
8355     html: false,
8356     
8357     getAutoCreate : function(){
8358         
8359         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8360         
8361         var id = Roo.id();
8362         
8363         var cfg = {};
8364         
8365         var input =  {
8366             tag: 'textarea',
8367             id : id,
8368             warp : this.warp,
8369             rows : this.rows,
8370             value : this.value || '',
8371             html: this.html || '',
8372             cls : 'form-control',
8373             placeholder : this.placeholder || '' 
8374             
8375         };
8376         
8377         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8378             input.maxLength = this.maxLength;
8379         }
8380         
8381         if(this.resize){
8382             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8383         }
8384         
8385         if(this.cols){
8386             input.cols = this.cols;
8387         }
8388         
8389         if (this.readOnly) {
8390             input.readonly = true;
8391         }
8392         
8393         if (this.name) {
8394             input.name = this.name;
8395         }
8396         
8397         if (this.size) {
8398             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8399         }
8400         
8401         var settings=this;
8402         ['xs','sm','md','lg'].map(function(size){
8403             if (settings[size]) {
8404                 cfg.cls += ' col-' + size + '-' + settings[size];
8405             }
8406         });
8407         
8408         var inputblock = input;
8409         
8410         if(this.hasFeedback && !this.allowBlank){
8411             
8412             var feedback = {
8413                 tag: 'span',
8414                 cls: 'glyphicon form-control-feedback'
8415             };
8416
8417             inputblock = {
8418                 cls : 'has-feedback',
8419                 cn :  [
8420                     input,
8421                     feedback
8422                 ] 
8423             };  
8424         }
8425         
8426         
8427         if (this.before || this.after) {
8428             
8429             inputblock = {
8430                 cls : 'input-group',
8431                 cn :  [] 
8432             };
8433             if (this.before) {
8434                 inputblock.cn.push({
8435                     tag :'span',
8436                     cls : 'input-group-addon',
8437                     html : this.before
8438                 });
8439             }
8440             
8441             inputblock.cn.push(input);
8442             
8443             if(this.hasFeedback && !this.allowBlank){
8444                 inputblock.cls += ' has-feedback';
8445                 inputblock.cn.push(feedback);
8446             }
8447             
8448             if (this.after) {
8449                 inputblock.cn.push({
8450                     tag :'span',
8451                     cls : 'input-group-addon',
8452                     html : this.after
8453                 });
8454             }
8455             
8456         }
8457         
8458         if (align ==='left' && this.fieldLabel.length) {
8459                 Roo.log("left and has label");
8460                 cfg.cn = [
8461                     
8462                     {
8463                         tag: 'label',
8464                         'for' :  id,
8465                         cls : 'control-label col-sm-' + this.labelWidth,
8466                         html : this.fieldLabel
8467                         
8468                     },
8469                     {
8470                         cls : "col-sm-" + (12 - this.labelWidth), 
8471                         cn: [
8472                             inputblock
8473                         ]
8474                     }
8475                     
8476                 ];
8477         } else if ( this.fieldLabel.length) {
8478                 Roo.log(" label");
8479                  cfg.cn = [
8480                    
8481                     {
8482                         tag: 'label',
8483                         //cls : 'input-group-addon',
8484                         html : this.fieldLabel
8485                         
8486                     },
8487                     
8488                     inputblock
8489                     
8490                 ];
8491
8492         } else {
8493             
8494                    Roo.log(" no label && no align");
8495                 cfg.cn = [
8496                     
8497                         inputblock
8498                     
8499                 ];
8500                 
8501                 
8502         }
8503         
8504         if (this.disabled) {
8505             input.disabled=true;
8506         }
8507         
8508         return cfg;
8509         
8510     },
8511     /**
8512      * return the real textarea element.
8513      */
8514     inputEl: function ()
8515     {
8516         return this.el.select('textarea.form-control',true).first();
8517     }
8518 });
8519
8520  
8521 /*
8522  * - LGPL
8523  *
8524  * trigger field - base class for combo..
8525  * 
8526  */
8527  
8528 /**
8529  * @class Roo.bootstrap.TriggerField
8530  * @extends Roo.bootstrap.Input
8531  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8532  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8533  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8534  * for which you can provide a custom implementation.  For example:
8535  * <pre><code>
8536 var trigger = new Roo.bootstrap.TriggerField();
8537 trigger.onTriggerClick = myTriggerFn;
8538 trigger.applyTo('my-field');
8539 </code></pre>
8540  *
8541  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8542  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8543  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8544  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8545  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8546
8547  * @constructor
8548  * Create a new TriggerField.
8549  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8550  * to the base TextField)
8551  */
8552 Roo.bootstrap.TriggerField = function(config){
8553     this.mimicing = false;
8554     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8555 };
8556
8557 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8558     /**
8559      * @cfg {String} triggerClass A CSS class to apply to the trigger
8560      */
8561      /**
8562      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8563      */
8564     hideTrigger:false,
8565
8566     /**
8567      * @cfg {Boolean} removable (true|false) special filter default false
8568      */
8569     removable : false,
8570     
8571     /** @cfg {Boolean} grow @hide */
8572     /** @cfg {Number} growMin @hide */
8573     /** @cfg {Number} growMax @hide */
8574
8575     /**
8576      * @hide 
8577      * @method
8578      */
8579     autoSize: Roo.emptyFn,
8580     // private
8581     monitorTab : true,
8582     // private
8583     deferHeight : true,
8584
8585     
8586     actionMode : 'wrap',
8587     
8588     caret : false,
8589     
8590     
8591     getAutoCreate : function(){
8592        
8593         var align = this.labelAlign || this.parentLabelAlign();
8594         
8595         var id = Roo.id();
8596         
8597         var cfg = {
8598             cls: 'form-group' //input-group
8599         };
8600         
8601         
8602         var input =  {
8603             tag: 'input',
8604             id : id,
8605             type : this.inputType,
8606             cls : 'form-control',
8607             autocomplete: 'new-password',
8608             placeholder : this.placeholder || '' 
8609             
8610         };
8611         if (this.name) {
8612             input.name = this.name;
8613         }
8614         if (this.size) {
8615             input.cls += ' input-' + this.size;
8616         }
8617         
8618         if (this.disabled) {
8619             input.disabled=true;
8620         }
8621         
8622         var inputblock = input;
8623         
8624         if(this.hasFeedback && !this.allowBlank){
8625             
8626             var feedback = {
8627                 tag: 'span',
8628                 cls: 'glyphicon form-control-feedback'
8629             };
8630             
8631             if(this.removable && !this.editable && !this.tickable){
8632                 inputblock = {
8633                     cls : 'has-feedback',
8634                     cn :  [
8635                         inputblock,
8636                         {
8637                             tag: 'button',
8638                             html : 'x',
8639                             cls : 'roo-combo-removable-btn close'
8640                         },
8641                         feedback
8642                     ] 
8643                 };
8644             } else {
8645                 inputblock = {
8646                     cls : 'has-feedback',
8647                     cn :  [
8648                         inputblock,
8649                         feedback
8650                     ] 
8651                 };
8652             }
8653
8654         } else {
8655             if(this.removable && !this.editable && !this.tickable){
8656                 inputblock = {
8657                     cls : 'roo-removable',
8658                     cn :  [
8659                         inputblock,
8660                         {
8661                             tag: 'button',
8662                             html : 'x',
8663                             cls : 'roo-combo-removable-btn close'
8664                         }
8665                     ] 
8666                 };
8667             }
8668         }
8669         
8670         if (this.before || this.after) {
8671             
8672             inputblock = {
8673                 cls : 'input-group',
8674                 cn :  [] 
8675             };
8676             if (this.before) {
8677                 inputblock.cn.push({
8678                     tag :'span',
8679                     cls : 'input-group-addon',
8680                     html : this.before
8681                 });
8682             }
8683             
8684             inputblock.cn.push(input);
8685             
8686             if(this.hasFeedback && !this.allowBlank){
8687                 inputblock.cls += ' has-feedback';
8688                 inputblock.cn.push(feedback);
8689             }
8690             
8691             if (this.after) {
8692                 inputblock.cn.push({
8693                     tag :'span',
8694                     cls : 'input-group-addon',
8695                     html : this.after
8696                 });
8697             }
8698             
8699         };
8700         
8701         var box = {
8702             tag: 'div',
8703             cn: [
8704                 {
8705                     tag: 'input',
8706                     type : 'hidden',
8707                     cls: 'form-hidden-field'
8708                 },
8709                 inputblock
8710             ]
8711             
8712         };
8713         
8714         if(this.multiple){
8715             Roo.log('multiple');
8716             
8717             box = {
8718                 tag: 'div',
8719                 cn: [
8720                     {
8721                         tag: 'input',
8722                         type : 'hidden',
8723                         cls: 'form-hidden-field'
8724                     },
8725                     {
8726                         tag: 'ul',
8727                         cls: 'select2-choices',
8728                         cn:[
8729                             {
8730                                 tag: 'li',
8731                                 cls: 'select2-search-field',
8732                                 cn: [
8733
8734                                     inputblock
8735                                 ]
8736                             }
8737                         ]
8738                     }
8739                 ]
8740             }
8741         };
8742         
8743         var combobox = {
8744             cls: 'select2-container input-group',
8745             cn: [
8746                 box
8747 //                {
8748 //                    tag: 'ul',
8749 //                    cls: 'typeahead typeahead-long dropdown-menu',
8750 //                    style: 'display:none'
8751 //                }
8752             ]
8753         };
8754         
8755         if(!this.multiple && this.showToggleBtn){
8756             
8757             var caret = {
8758                         tag: 'span',
8759                         cls: 'caret'
8760              };
8761             if (this.caret != false) {
8762                 caret = {
8763                      tag: 'i',
8764                      cls: 'fa fa-' + this.caret
8765                 };
8766                 
8767             }
8768             
8769             combobox.cn.push({
8770                 tag :'span',
8771                 cls : 'input-group-addon btn dropdown-toggle',
8772                 cn : [
8773                     caret,
8774                     {
8775                         tag: 'span',
8776                         cls: 'combobox-clear',
8777                         cn  : [
8778                             {
8779                                 tag : 'i',
8780                                 cls: 'icon-remove'
8781                             }
8782                         ]
8783                     }
8784                 ]
8785
8786             })
8787         }
8788         
8789         if(this.multiple){
8790             combobox.cls += ' select2-container-multi';
8791         }
8792         
8793         if (align ==='left' && this.fieldLabel.length) {
8794             
8795                 Roo.log("left and has label");
8796                 cfg.cn = [
8797                     
8798                     {
8799                         tag: 'label',
8800                         'for' :  id,
8801                         cls : 'control-label col-sm-' + this.labelWidth,
8802                         html : this.fieldLabel
8803                         
8804                     },
8805                     {
8806                         cls : "col-sm-" + (12 - this.labelWidth), 
8807                         cn: [
8808                             combobox
8809                         ]
8810                     }
8811                     
8812                 ];
8813         } else if ( this.fieldLabel.length) {
8814                 Roo.log(" label");
8815                  cfg.cn = [
8816                    
8817                     {
8818                         tag: 'label',
8819                         //cls : 'input-group-addon',
8820                         html : this.fieldLabel
8821                         
8822                     },
8823                     
8824                     combobox
8825                     
8826                 ];
8827
8828         } else {
8829             
8830                 Roo.log(" no label && no align");
8831                 cfg = combobox
8832                      
8833                 
8834         }
8835          
8836         var settings=this;
8837         ['xs','sm','md','lg'].map(function(size){
8838             if (settings[size]) {
8839                 cfg.cls += ' col-' + size + '-' + settings[size];
8840             }
8841         });
8842         Roo.log(cfg);
8843         return cfg;
8844         
8845     },
8846     
8847     
8848     
8849     // private
8850     onResize : function(w, h){
8851 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8852 //        if(typeof w == 'number'){
8853 //            var x = w - this.trigger.getWidth();
8854 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8855 //            this.trigger.setStyle('left', x+'px');
8856 //        }
8857     },
8858
8859     // private
8860     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8861
8862     // private
8863     getResizeEl : function(){
8864         return this.inputEl();
8865     },
8866
8867     // private
8868     getPositionEl : function(){
8869         return this.inputEl();
8870     },
8871
8872     // private
8873     alignErrorIcon : function(){
8874         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8875     },
8876
8877     // private
8878     initEvents : function(){
8879         
8880         this.createList();
8881         
8882         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8883         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8884         if(!this.multiple && this.showToggleBtn){
8885             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8886             if(this.hideTrigger){
8887                 this.trigger.setDisplayed(false);
8888             }
8889             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8890         }
8891         
8892         if(this.multiple){
8893             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8894         }
8895         
8896         if(this.removable && !this.editable && !this.tickable){
8897             var close = this.closeTriggerEl();
8898             
8899             if(close){
8900                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8901                 close.on('click', this.removeBtnClick, this, close);
8902             }
8903         }
8904         
8905         //this.trigger.addClassOnOver('x-form-trigger-over');
8906         //this.trigger.addClassOnClick('x-form-trigger-click');
8907         
8908         //if(!this.width){
8909         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8910         //}
8911     },
8912     
8913     closeTriggerEl : function()
8914     {
8915         var close = this.el.select('.roo-combo-removable-btn', true).first();
8916         return close ? close : false;
8917     },
8918     
8919     removeBtnClick : function(e, h, el)
8920     {
8921         e.preventDefault();
8922         
8923         if(this.fireEvent("remove", this) !== false){
8924             this.reset();
8925         }
8926     },
8927     
8928     createList : function()
8929     {
8930         this.list = Roo.get(document.body).createChild({
8931             tag: 'ul',
8932             cls: 'typeahead typeahead-long dropdown-menu',
8933             style: 'display:none'
8934         });
8935         
8936         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8937         
8938     },
8939
8940     // private
8941     initTrigger : function(){
8942        
8943     },
8944
8945     // private
8946     onDestroy : function(){
8947         if(this.trigger){
8948             this.trigger.removeAllListeners();
8949           //  this.trigger.remove();
8950         }
8951         //if(this.wrap){
8952         //    this.wrap.remove();
8953         //}
8954         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8955     },
8956
8957     // private
8958     onFocus : function(){
8959         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8960         /*
8961         if(!this.mimicing){
8962             this.wrap.addClass('x-trigger-wrap-focus');
8963             this.mimicing = true;
8964             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8965             if(this.monitorTab){
8966                 this.el.on("keydown", this.checkTab, this);
8967             }
8968         }
8969         */
8970     },
8971
8972     // private
8973     checkTab : function(e){
8974         if(e.getKey() == e.TAB){
8975             this.triggerBlur();
8976         }
8977     },
8978
8979     // private
8980     onBlur : function(){
8981         // do nothing
8982     },
8983
8984     // private
8985     mimicBlur : function(e, t){
8986         /*
8987         if(!this.wrap.contains(t) && this.validateBlur()){
8988             this.triggerBlur();
8989         }
8990         */
8991     },
8992
8993     // private
8994     triggerBlur : function(){
8995         this.mimicing = false;
8996         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8997         if(this.monitorTab){
8998             this.el.un("keydown", this.checkTab, this);
8999         }
9000         //this.wrap.removeClass('x-trigger-wrap-focus');
9001         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9002     },
9003
9004     // private
9005     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9006     validateBlur : function(e, t){
9007         return true;
9008     },
9009
9010     // private
9011     onDisable : function(){
9012         this.inputEl().dom.disabled = true;
9013         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9014         //if(this.wrap){
9015         //    this.wrap.addClass('x-item-disabled');
9016         //}
9017     },
9018
9019     // private
9020     onEnable : function(){
9021         this.inputEl().dom.disabled = false;
9022         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9023         //if(this.wrap){
9024         //    this.el.removeClass('x-item-disabled');
9025         //}
9026     },
9027
9028     // private
9029     onShow : function(){
9030         var ae = this.getActionEl();
9031         
9032         if(ae){
9033             ae.dom.style.display = '';
9034             ae.dom.style.visibility = 'visible';
9035         }
9036     },
9037
9038     // private
9039     
9040     onHide : function(){
9041         var ae = this.getActionEl();
9042         ae.dom.style.display = 'none';
9043     },
9044
9045     /**
9046      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9047      * by an implementing function.
9048      * @method
9049      * @param {EventObject} e
9050      */
9051     onTriggerClick : Roo.emptyFn
9052 });
9053  /*
9054  * Based on:
9055  * Ext JS Library 1.1.1
9056  * Copyright(c) 2006-2007, Ext JS, LLC.
9057  *
9058  * Originally Released Under LGPL - original licence link has changed is not relivant.
9059  *
9060  * Fork - LGPL
9061  * <script type="text/javascript">
9062  */
9063
9064
9065 /**
9066  * @class Roo.data.SortTypes
9067  * @singleton
9068  * Defines the default sorting (casting?) comparison functions used when sorting data.
9069  */
9070 Roo.data.SortTypes = {
9071     /**
9072      * Default sort that does nothing
9073      * @param {Mixed} s The value being converted
9074      * @return {Mixed} The comparison value
9075      */
9076     none : function(s){
9077         return s;
9078     },
9079     
9080     /**
9081      * The regular expression used to strip tags
9082      * @type {RegExp}
9083      * @property
9084      */
9085     stripTagsRE : /<\/?[^>]+>/gi,
9086     
9087     /**
9088      * Strips all HTML tags to sort on text only
9089      * @param {Mixed} s The value being converted
9090      * @return {String} The comparison value
9091      */
9092     asText : function(s){
9093         return String(s).replace(this.stripTagsRE, "");
9094     },
9095     
9096     /**
9097      * Strips all HTML tags to sort on text only - Case insensitive
9098      * @param {Mixed} s The value being converted
9099      * @return {String} The comparison value
9100      */
9101     asUCText : function(s){
9102         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9103     },
9104     
9105     /**
9106      * Case insensitive string
9107      * @param {Mixed} s The value being converted
9108      * @return {String} The comparison value
9109      */
9110     asUCString : function(s) {
9111         return String(s).toUpperCase();
9112     },
9113     
9114     /**
9115      * Date sorting
9116      * @param {Mixed} s The value being converted
9117      * @return {Number} The comparison value
9118      */
9119     asDate : function(s) {
9120         if(!s){
9121             return 0;
9122         }
9123         if(s instanceof Date){
9124             return s.getTime();
9125         }
9126         return Date.parse(String(s));
9127     },
9128     
9129     /**
9130      * Float sorting
9131      * @param {Mixed} s The value being converted
9132      * @return {Float} The comparison value
9133      */
9134     asFloat : function(s) {
9135         var val = parseFloat(String(s).replace(/,/g, ""));
9136         if(isNaN(val)) val = 0;
9137         return val;
9138     },
9139     
9140     /**
9141      * Integer sorting
9142      * @param {Mixed} s The value being converted
9143      * @return {Number} The comparison value
9144      */
9145     asInt : function(s) {
9146         var val = parseInt(String(s).replace(/,/g, ""));
9147         if(isNaN(val)) val = 0;
9148         return val;
9149     }
9150 };/*
9151  * Based on:
9152  * Ext JS Library 1.1.1
9153  * Copyright(c) 2006-2007, Ext JS, LLC.
9154  *
9155  * Originally Released Under LGPL - original licence link has changed is not relivant.
9156  *
9157  * Fork - LGPL
9158  * <script type="text/javascript">
9159  */
9160
9161 /**
9162 * @class Roo.data.Record
9163  * Instances of this class encapsulate both record <em>definition</em> information, and record
9164  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9165  * to access Records cached in an {@link Roo.data.Store} object.<br>
9166  * <p>
9167  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9168  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9169  * objects.<br>
9170  * <p>
9171  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9172  * @constructor
9173  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9174  * {@link #create}. The parameters are the same.
9175  * @param {Array} data An associative Array of data values keyed by the field name.
9176  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9177  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9178  * not specified an integer id is generated.
9179  */
9180 Roo.data.Record = function(data, id){
9181     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9182     this.data = data;
9183 };
9184
9185 /**
9186  * Generate a constructor for a specific record layout.
9187  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9188  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9189  * Each field definition object may contain the following properties: <ul>
9190  * <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,
9191  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9192  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9193  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9194  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9195  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9196  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9197  * this may be omitted.</p></li>
9198  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9199  * <ul><li>auto (Default, implies no conversion)</li>
9200  * <li>string</li>
9201  * <li>int</li>
9202  * <li>float</li>
9203  * <li>boolean</li>
9204  * <li>date</li></ul></p></li>
9205  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9206  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9207  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9208  * by the Reader into an object that will be stored in the Record. It is passed the
9209  * following parameters:<ul>
9210  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9211  * </ul></p></li>
9212  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9213  * </ul>
9214  * <br>usage:<br><pre><code>
9215 var TopicRecord = Roo.data.Record.create(
9216     {name: 'title', mapping: 'topic_title'},
9217     {name: 'author', mapping: 'username'},
9218     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9219     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9220     {name: 'lastPoster', mapping: 'user2'},
9221     {name: 'excerpt', mapping: 'post_text'}
9222 );
9223
9224 var myNewRecord = new TopicRecord({
9225     title: 'Do my job please',
9226     author: 'noobie',
9227     totalPosts: 1,
9228     lastPost: new Date(),
9229     lastPoster: 'Animal',
9230     excerpt: 'No way dude!'
9231 });
9232 myStore.add(myNewRecord);
9233 </code></pre>
9234  * @method create
9235  * @static
9236  */
9237 Roo.data.Record.create = function(o){
9238     var f = function(){
9239         f.superclass.constructor.apply(this, arguments);
9240     };
9241     Roo.extend(f, Roo.data.Record);
9242     var p = f.prototype;
9243     p.fields = new Roo.util.MixedCollection(false, function(field){
9244         return field.name;
9245     });
9246     for(var i = 0, len = o.length; i < len; i++){
9247         p.fields.add(new Roo.data.Field(o[i]));
9248     }
9249     f.getField = function(name){
9250         return p.fields.get(name);  
9251     };
9252     return f;
9253 };
9254
9255 Roo.data.Record.AUTO_ID = 1000;
9256 Roo.data.Record.EDIT = 'edit';
9257 Roo.data.Record.REJECT = 'reject';
9258 Roo.data.Record.COMMIT = 'commit';
9259
9260 Roo.data.Record.prototype = {
9261     /**
9262      * Readonly flag - true if this record has been modified.
9263      * @type Boolean
9264      */
9265     dirty : false,
9266     editing : false,
9267     error: null,
9268     modified: null,
9269
9270     // private
9271     join : function(store){
9272         this.store = store;
9273     },
9274
9275     /**
9276      * Set the named field to the specified value.
9277      * @param {String} name The name of the field to set.
9278      * @param {Object} value The value to set the field to.
9279      */
9280     set : function(name, value){
9281         if(this.data[name] == value){
9282             return;
9283         }
9284         this.dirty = true;
9285         if(!this.modified){
9286             this.modified = {};
9287         }
9288         if(typeof this.modified[name] == 'undefined'){
9289             this.modified[name] = this.data[name];
9290         }
9291         this.data[name] = value;
9292         if(!this.editing && this.store){
9293             this.store.afterEdit(this);
9294         }       
9295     },
9296
9297     /**
9298      * Get the value of the named field.
9299      * @param {String} name The name of the field to get the value of.
9300      * @return {Object} The value of the field.
9301      */
9302     get : function(name){
9303         return this.data[name]; 
9304     },
9305
9306     // private
9307     beginEdit : function(){
9308         this.editing = true;
9309         this.modified = {}; 
9310     },
9311
9312     // private
9313     cancelEdit : function(){
9314         this.editing = false;
9315         delete this.modified;
9316     },
9317
9318     // private
9319     endEdit : function(){
9320         this.editing = false;
9321         if(this.dirty && this.store){
9322             this.store.afterEdit(this);
9323         }
9324     },
9325
9326     /**
9327      * Usually called by the {@link Roo.data.Store} which owns the Record.
9328      * Rejects all changes made to the Record since either creation, or the last commit operation.
9329      * Modified fields are reverted to their original values.
9330      * <p>
9331      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9332      * of reject operations.
9333      */
9334     reject : function(){
9335         var m = this.modified;
9336         for(var n in m){
9337             if(typeof m[n] != "function"){
9338                 this.data[n] = m[n];
9339             }
9340         }
9341         this.dirty = false;
9342         delete this.modified;
9343         this.editing = false;
9344         if(this.store){
9345             this.store.afterReject(this);
9346         }
9347     },
9348
9349     /**
9350      * Usually called by the {@link Roo.data.Store} which owns the Record.
9351      * Commits all changes made to the Record since either creation, or the last commit operation.
9352      * <p>
9353      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9354      * of commit operations.
9355      */
9356     commit : function(){
9357         this.dirty = false;
9358         delete this.modified;
9359         this.editing = false;
9360         if(this.store){
9361             this.store.afterCommit(this);
9362         }
9363     },
9364
9365     // private
9366     hasError : function(){
9367         return this.error != null;
9368     },
9369
9370     // private
9371     clearError : function(){
9372         this.error = null;
9373     },
9374
9375     /**
9376      * Creates a copy of this record.
9377      * @param {String} id (optional) A new record id if you don't want to use this record's id
9378      * @return {Record}
9379      */
9380     copy : function(newId) {
9381         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9382     }
9383 };/*
9384  * Based on:
9385  * Ext JS Library 1.1.1
9386  * Copyright(c) 2006-2007, Ext JS, LLC.
9387  *
9388  * Originally Released Under LGPL - original licence link has changed is not relivant.
9389  *
9390  * Fork - LGPL
9391  * <script type="text/javascript">
9392  */
9393
9394
9395
9396 /**
9397  * @class Roo.data.Store
9398  * @extends Roo.util.Observable
9399  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9400  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9401  * <p>
9402  * 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
9403  * has no knowledge of the format of the data returned by the Proxy.<br>
9404  * <p>
9405  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9406  * instances from the data object. These records are cached and made available through accessor functions.
9407  * @constructor
9408  * Creates a new Store.
9409  * @param {Object} config A config object containing the objects needed for the Store to access data,
9410  * and read the data into Records.
9411  */
9412 Roo.data.Store = function(config){
9413     this.data = new Roo.util.MixedCollection(false);
9414     this.data.getKey = function(o){
9415         return o.id;
9416     };
9417     this.baseParams = {};
9418     // private
9419     this.paramNames = {
9420         "start" : "start",
9421         "limit" : "limit",
9422         "sort" : "sort",
9423         "dir" : "dir",
9424         "multisort" : "_multisort"
9425     };
9426
9427     if(config && config.data){
9428         this.inlineData = config.data;
9429         delete config.data;
9430     }
9431
9432     Roo.apply(this, config);
9433     
9434     if(this.reader){ // reader passed
9435         this.reader = Roo.factory(this.reader, Roo.data);
9436         this.reader.xmodule = this.xmodule || false;
9437         if(!this.recordType){
9438             this.recordType = this.reader.recordType;
9439         }
9440         if(this.reader.onMetaChange){
9441             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9442         }
9443     }
9444
9445     if(this.recordType){
9446         this.fields = this.recordType.prototype.fields;
9447     }
9448     this.modified = [];
9449
9450     this.addEvents({
9451         /**
9452          * @event datachanged
9453          * Fires when the data cache has changed, and a widget which is using this Store
9454          * as a Record cache should refresh its view.
9455          * @param {Store} this
9456          */
9457         datachanged : true,
9458         /**
9459          * @event metachange
9460          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9461          * @param {Store} this
9462          * @param {Object} meta The JSON metadata
9463          */
9464         metachange : true,
9465         /**
9466          * @event add
9467          * Fires when Records have been added to the Store
9468          * @param {Store} this
9469          * @param {Roo.data.Record[]} records The array of Records added
9470          * @param {Number} index The index at which the record(s) were added
9471          */
9472         add : true,
9473         /**
9474          * @event remove
9475          * Fires when a Record has been removed from the Store
9476          * @param {Store} this
9477          * @param {Roo.data.Record} record The Record that was removed
9478          * @param {Number} index The index at which the record was removed
9479          */
9480         remove : true,
9481         /**
9482          * @event update
9483          * Fires when a Record has been updated
9484          * @param {Store} this
9485          * @param {Roo.data.Record} record The Record that was updated
9486          * @param {String} operation The update operation being performed.  Value may be one of:
9487          * <pre><code>
9488  Roo.data.Record.EDIT
9489  Roo.data.Record.REJECT
9490  Roo.data.Record.COMMIT
9491          * </code></pre>
9492          */
9493         update : true,
9494         /**
9495          * @event clear
9496          * Fires when the data cache has been cleared.
9497          * @param {Store} this
9498          */
9499         clear : true,
9500         /**
9501          * @event beforeload
9502          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9503          * the load action will be canceled.
9504          * @param {Store} this
9505          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9506          */
9507         beforeload : true,
9508         /**
9509          * @event beforeloadadd
9510          * Fires after a new set of Records has been loaded.
9511          * @param {Store} this
9512          * @param {Roo.data.Record[]} records The Records that were loaded
9513          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9514          */
9515         beforeloadadd : true,
9516         /**
9517          * @event load
9518          * Fires after a new set of Records has been loaded, before they are added to the store.
9519          * @param {Store} this
9520          * @param {Roo.data.Record[]} records The Records that were loaded
9521          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9522          * @params {Object} return from reader
9523          */
9524         load : true,
9525         /**
9526          * @event loadexception
9527          * Fires if an exception occurs in the Proxy during loading.
9528          * Called with the signature of the Proxy's "loadexception" event.
9529          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9530          * 
9531          * @param {Proxy} 
9532          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9533          * @param {Object} load options 
9534          * @param {Object} jsonData from your request (normally this contains the Exception)
9535          */
9536         loadexception : true
9537     });
9538     
9539     if(this.proxy){
9540         this.proxy = Roo.factory(this.proxy, Roo.data);
9541         this.proxy.xmodule = this.xmodule || false;
9542         this.relayEvents(this.proxy,  ["loadexception"]);
9543     }
9544     this.sortToggle = {};
9545     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9546
9547     Roo.data.Store.superclass.constructor.call(this);
9548
9549     if(this.inlineData){
9550         this.loadData(this.inlineData);
9551         delete this.inlineData;
9552     }
9553 };
9554
9555 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9556      /**
9557     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9558     * without a remote query - used by combo/forms at present.
9559     */
9560     
9561     /**
9562     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9563     */
9564     /**
9565     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9566     */
9567     /**
9568     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9569     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9570     */
9571     /**
9572     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9573     * on any HTTP request
9574     */
9575     /**
9576     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9577     */
9578     /**
9579     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9580     */
9581     multiSort: false,
9582     /**
9583     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9584     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9585     */
9586     remoteSort : false,
9587
9588     /**
9589     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9590      * loaded or when a record is removed. (defaults to false).
9591     */
9592     pruneModifiedRecords : false,
9593
9594     // private
9595     lastOptions : null,
9596
9597     /**
9598      * Add Records to the Store and fires the add event.
9599      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9600      */
9601     add : function(records){
9602         records = [].concat(records);
9603         for(var i = 0, len = records.length; i < len; i++){
9604             records[i].join(this);
9605         }
9606         var index = this.data.length;
9607         this.data.addAll(records);
9608         this.fireEvent("add", this, records, index);
9609     },
9610
9611     /**
9612      * Remove a Record from the Store and fires the remove event.
9613      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9614      */
9615     remove : function(record){
9616         var index = this.data.indexOf(record);
9617         this.data.removeAt(index);
9618         if(this.pruneModifiedRecords){
9619             this.modified.remove(record);
9620         }
9621         this.fireEvent("remove", this, record, index);
9622     },
9623
9624     /**
9625      * Remove all Records from the Store and fires the clear event.
9626      */
9627     removeAll : function(){
9628         this.data.clear();
9629         if(this.pruneModifiedRecords){
9630             this.modified = [];
9631         }
9632         this.fireEvent("clear", this);
9633     },
9634
9635     /**
9636      * Inserts Records to the Store at the given index and fires the add event.
9637      * @param {Number} index The start index at which to insert the passed Records.
9638      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9639      */
9640     insert : function(index, records){
9641         records = [].concat(records);
9642         for(var i = 0, len = records.length; i < len; i++){
9643             this.data.insert(index, records[i]);
9644             records[i].join(this);
9645         }
9646         this.fireEvent("add", this, records, index);
9647     },
9648
9649     /**
9650      * Get the index within the cache of the passed Record.
9651      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9652      * @return {Number} The index of the passed Record. Returns -1 if not found.
9653      */
9654     indexOf : function(record){
9655         return this.data.indexOf(record);
9656     },
9657
9658     /**
9659      * Get the index within the cache of the Record with the passed id.
9660      * @param {String} id The id of the Record to find.
9661      * @return {Number} The index of the Record. Returns -1 if not found.
9662      */
9663     indexOfId : function(id){
9664         return this.data.indexOfKey(id);
9665     },
9666
9667     /**
9668      * Get the Record with the specified id.
9669      * @param {String} id The id of the Record to find.
9670      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9671      */
9672     getById : function(id){
9673         return this.data.key(id);
9674     },
9675
9676     /**
9677      * Get the Record at the specified index.
9678      * @param {Number} index The index of the Record to find.
9679      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9680      */
9681     getAt : function(index){
9682         return this.data.itemAt(index);
9683     },
9684
9685     /**
9686      * Returns a range of Records between specified indices.
9687      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9688      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9689      * @return {Roo.data.Record[]} An array of Records
9690      */
9691     getRange : function(start, end){
9692         return this.data.getRange(start, end);
9693     },
9694
9695     // private
9696     storeOptions : function(o){
9697         o = Roo.apply({}, o);
9698         delete o.callback;
9699         delete o.scope;
9700         this.lastOptions = o;
9701     },
9702
9703     /**
9704      * Loads the Record cache from the configured Proxy using the configured Reader.
9705      * <p>
9706      * If using remote paging, then the first load call must specify the <em>start</em>
9707      * and <em>limit</em> properties in the options.params property to establish the initial
9708      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9709      * <p>
9710      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9711      * and this call will return before the new data has been loaded. Perform any post-processing
9712      * in a callback function, or in a "load" event handler.</strong>
9713      * <p>
9714      * @param {Object} options An object containing properties which control loading options:<ul>
9715      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9716      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9717      * passed the following arguments:<ul>
9718      * <li>r : Roo.data.Record[]</li>
9719      * <li>options: Options object from the load call</li>
9720      * <li>success: Boolean success indicator</li></ul></li>
9721      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9722      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9723      * </ul>
9724      */
9725     load : function(options){
9726         options = options || {};
9727         if(this.fireEvent("beforeload", this, options) !== false){
9728             this.storeOptions(options);
9729             var p = Roo.apply(options.params || {}, this.baseParams);
9730             // if meta was not loaded from remote source.. try requesting it.
9731             if (!this.reader.metaFromRemote) {
9732                 p._requestMeta = 1;
9733             }
9734             if(this.sortInfo && this.remoteSort){
9735                 var pn = this.paramNames;
9736                 p[pn["sort"]] = this.sortInfo.field;
9737                 p[pn["dir"]] = this.sortInfo.direction;
9738             }
9739             if (this.multiSort) {
9740                 var pn = this.paramNames;
9741                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9742             }
9743             
9744             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9745         }
9746     },
9747
9748     /**
9749      * Reloads the Record cache from the configured Proxy using the configured Reader and
9750      * the options from the last load operation performed.
9751      * @param {Object} options (optional) An object containing properties which may override the options
9752      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9753      * the most recently used options are reused).
9754      */
9755     reload : function(options){
9756         this.load(Roo.applyIf(options||{}, this.lastOptions));
9757     },
9758
9759     // private
9760     // Called as a callback by the Reader during a load operation.
9761     loadRecords : function(o, options, success){
9762         if(!o || success === false){
9763             if(success !== false){
9764                 this.fireEvent("load", this, [], options, o);
9765             }
9766             if(options.callback){
9767                 options.callback.call(options.scope || this, [], options, false);
9768             }
9769             return;
9770         }
9771         // if data returned failure - throw an exception.
9772         if (o.success === false) {
9773             // show a message if no listener is registered.
9774             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9775                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9776             }
9777             // loadmask wil be hooked into this..
9778             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9779             return;
9780         }
9781         var r = o.records, t = o.totalRecords || r.length;
9782         
9783         this.fireEvent("beforeloadadd", this, r, options, o);
9784         
9785         if(!options || options.add !== true){
9786             if(this.pruneModifiedRecords){
9787                 this.modified = [];
9788             }
9789             for(var i = 0, len = r.length; i < len; i++){
9790                 r[i].join(this);
9791             }
9792             if(this.snapshot){
9793                 this.data = this.snapshot;
9794                 delete this.snapshot;
9795             }
9796             this.data.clear();
9797             this.data.addAll(r);
9798             this.totalLength = t;
9799             this.applySort();
9800             this.fireEvent("datachanged", this);
9801         }else{
9802             this.totalLength = Math.max(t, this.data.length+r.length);
9803             this.add(r);
9804         }
9805         this.fireEvent("load", this, r, options, o);
9806         if(options.callback){
9807             options.callback.call(options.scope || this, r, options, true);
9808         }
9809     },
9810
9811
9812     /**
9813      * Loads data from a passed data block. A Reader which understands the format of the data
9814      * must have been configured in the constructor.
9815      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9816      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9817      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9818      */
9819     loadData : function(o, append){
9820         var r = this.reader.readRecords(o);
9821         this.loadRecords(r, {add: append}, true);
9822     },
9823
9824     /**
9825      * Gets the number of cached records.
9826      * <p>
9827      * <em>If using paging, this may not be the total size of the dataset. If the data object
9828      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9829      * the data set size</em>
9830      */
9831     getCount : function(){
9832         return this.data.length || 0;
9833     },
9834
9835     /**
9836      * Gets the total number of records in the dataset as returned by the server.
9837      * <p>
9838      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9839      * the dataset size</em>
9840      */
9841     getTotalCount : function(){
9842         return this.totalLength || 0;
9843     },
9844
9845     /**
9846      * Returns the sort state of the Store as an object with two properties:
9847      * <pre><code>
9848  field {String} The name of the field by which the Records are sorted
9849  direction {String} The sort order, "ASC" or "DESC"
9850      * </code></pre>
9851      */
9852     getSortState : function(){
9853         return this.sortInfo;
9854     },
9855
9856     // private
9857     applySort : function(){
9858         if(this.sortInfo && !this.remoteSort){
9859             var s = this.sortInfo, f = s.field;
9860             var st = this.fields.get(f).sortType;
9861             var fn = function(r1, r2){
9862                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9863                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9864             };
9865             this.data.sort(s.direction, fn);
9866             if(this.snapshot && this.snapshot != this.data){
9867                 this.snapshot.sort(s.direction, fn);
9868             }
9869         }
9870     },
9871
9872     /**
9873      * Sets the default sort column and order to be used by the next load operation.
9874      * @param {String} fieldName The name of the field to sort by.
9875      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9876      */
9877     setDefaultSort : function(field, dir){
9878         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9879     },
9880
9881     /**
9882      * Sort the Records.
9883      * If remote sorting is used, the sort is performed on the server, and the cache is
9884      * reloaded. If local sorting is used, the cache is sorted internally.
9885      * @param {String} fieldName The name of the field to sort by.
9886      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9887      */
9888     sort : function(fieldName, dir){
9889         var f = this.fields.get(fieldName);
9890         if(!dir){
9891             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9892             
9893             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9894                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9895             }else{
9896                 dir = f.sortDir;
9897             }
9898         }
9899         this.sortToggle[f.name] = dir;
9900         this.sortInfo = {field: f.name, direction: dir};
9901         if(!this.remoteSort){
9902             this.applySort();
9903             this.fireEvent("datachanged", this);
9904         }else{
9905             this.load(this.lastOptions);
9906         }
9907     },
9908
9909     /**
9910      * Calls the specified function for each of the Records in the cache.
9911      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9912      * Returning <em>false</em> aborts and exits the iteration.
9913      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9914      */
9915     each : function(fn, scope){
9916         this.data.each(fn, scope);
9917     },
9918
9919     /**
9920      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9921      * (e.g., during paging).
9922      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9923      */
9924     getModifiedRecords : function(){
9925         return this.modified;
9926     },
9927
9928     // private
9929     createFilterFn : function(property, value, anyMatch){
9930         if(!value.exec){ // not a regex
9931             value = String(value);
9932             if(value.length == 0){
9933                 return false;
9934             }
9935             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9936         }
9937         return function(r){
9938             return value.test(r.data[property]);
9939         };
9940     },
9941
9942     /**
9943      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9944      * @param {String} property A field on your records
9945      * @param {Number} start The record index to start at (defaults to 0)
9946      * @param {Number} end The last record index to include (defaults to length - 1)
9947      * @return {Number} The sum
9948      */
9949     sum : function(property, start, end){
9950         var rs = this.data.items, v = 0;
9951         start = start || 0;
9952         end = (end || end === 0) ? end : rs.length-1;
9953
9954         for(var i = start; i <= end; i++){
9955             v += (rs[i].data[property] || 0);
9956         }
9957         return v;
9958     },
9959
9960     /**
9961      * Filter the records by a specified property.
9962      * @param {String} field A field on your records
9963      * @param {String/RegExp} value Either a string that the field
9964      * should start with or a RegExp to test against the field
9965      * @param {Boolean} anyMatch True to match any part not just the beginning
9966      */
9967     filter : function(property, value, anyMatch){
9968         var fn = this.createFilterFn(property, value, anyMatch);
9969         return fn ? this.filterBy(fn) : this.clearFilter();
9970     },
9971
9972     /**
9973      * Filter by a function. The specified function will be called with each
9974      * record in this data source. If the function returns true the record is included,
9975      * otherwise it is filtered.
9976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9977      * @param {Object} scope (optional) The scope of the function (defaults to this)
9978      */
9979     filterBy : function(fn, scope){
9980         this.snapshot = this.snapshot || this.data;
9981         this.data = this.queryBy(fn, scope||this);
9982         this.fireEvent("datachanged", this);
9983     },
9984
9985     /**
9986      * Query the records by a specified property.
9987      * @param {String} field A field on your records
9988      * @param {String/RegExp} value Either a string that the field
9989      * should start with or a RegExp to test against the field
9990      * @param {Boolean} anyMatch True to match any part not just the beginning
9991      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9992      */
9993     query : function(property, value, anyMatch){
9994         var fn = this.createFilterFn(property, value, anyMatch);
9995         return fn ? this.queryBy(fn) : this.data.clone();
9996     },
9997
9998     /**
9999      * Query by a function. The specified function will be called with each
10000      * record in this data source. If the function returns true the record is included
10001      * in the results.
10002      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10003      * @param {Object} scope (optional) The scope of the function (defaults to this)
10004       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10005      **/
10006     queryBy : function(fn, scope){
10007         var data = this.snapshot || this.data;
10008         return data.filterBy(fn, scope||this);
10009     },
10010
10011     /**
10012      * Collects unique values for a particular dataIndex from this store.
10013      * @param {String} dataIndex The property to collect
10014      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10015      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10016      * @return {Array} An array of the unique values
10017      **/
10018     collect : function(dataIndex, allowNull, bypassFilter){
10019         var d = (bypassFilter === true && this.snapshot) ?
10020                 this.snapshot.items : this.data.items;
10021         var v, sv, r = [], l = {};
10022         for(var i = 0, len = d.length; i < len; i++){
10023             v = d[i].data[dataIndex];
10024             sv = String(v);
10025             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10026                 l[sv] = true;
10027                 r[r.length] = v;
10028             }
10029         }
10030         return r;
10031     },
10032
10033     /**
10034      * Revert to a view of the Record cache with no filtering applied.
10035      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10036      */
10037     clearFilter : function(suppressEvent){
10038         if(this.snapshot && this.snapshot != this.data){
10039             this.data = this.snapshot;
10040             delete this.snapshot;
10041             if(suppressEvent !== true){
10042                 this.fireEvent("datachanged", this);
10043             }
10044         }
10045     },
10046
10047     // private
10048     afterEdit : function(record){
10049         if(this.modified.indexOf(record) == -1){
10050             this.modified.push(record);
10051         }
10052         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10053     },
10054     
10055     // private
10056     afterReject : function(record){
10057         this.modified.remove(record);
10058         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10059     },
10060
10061     // private
10062     afterCommit : function(record){
10063         this.modified.remove(record);
10064         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10065     },
10066
10067     /**
10068      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10069      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10070      */
10071     commitChanges : function(){
10072         var m = this.modified.slice(0);
10073         this.modified = [];
10074         for(var i = 0, len = m.length; i < len; i++){
10075             m[i].commit();
10076         }
10077     },
10078
10079     /**
10080      * Cancel outstanding changes on all changed records.
10081      */
10082     rejectChanges : function(){
10083         var m = this.modified.slice(0);
10084         this.modified = [];
10085         for(var i = 0, len = m.length; i < len; i++){
10086             m[i].reject();
10087         }
10088     },
10089
10090     onMetaChange : function(meta, rtype, o){
10091         this.recordType = rtype;
10092         this.fields = rtype.prototype.fields;
10093         delete this.snapshot;
10094         this.sortInfo = meta.sortInfo || this.sortInfo;
10095         this.modified = [];
10096         this.fireEvent('metachange', this, this.reader.meta);
10097     },
10098     
10099     moveIndex : function(data, type)
10100     {
10101         var index = this.indexOf(data);
10102         
10103         var newIndex = index + type;
10104         
10105         this.remove(data);
10106         
10107         this.insert(newIndex, data);
10108         
10109     }
10110 });/*
10111  * Based on:
10112  * Ext JS Library 1.1.1
10113  * Copyright(c) 2006-2007, Ext JS, LLC.
10114  *
10115  * Originally Released Under LGPL - original licence link has changed is not relivant.
10116  *
10117  * Fork - LGPL
10118  * <script type="text/javascript">
10119  */
10120
10121 /**
10122  * @class Roo.data.SimpleStore
10123  * @extends Roo.data.Store
10124  * Small helper class to make creating Stores from Array data easier.
10125  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10126  * @cfg {Array} fields An array of field definition objects, or field name strings.
10127  * @cfg {Array} data The multi-dimensional array of data
10128  * @constructor
10129  * @param {Object} config
10130  */
10131 Roo.data.SimpleStore = function(config){
10132     Roo.data.SimpleStore.superclass.constructor.call(this, {
10133         isLocal : true,
10134         reader: new Roo.data.ArrayReader({
10135                 id: config.id
10136             },
10137             Roo.data.Record.create(config.fields)
10138         ),
10139         proxy : new Roo.data.MemoryProxy(config.data)
10140     });
10141     this.load();
10142 };
10143 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10144  * Based on:
10145  * Ext JS Library 1.1.1
10146  * Copyright(c) 2006-2007, Ext JS, LLC.
10147  *
10148  * Originally Released Under LGPL - original licence link has changed is not relivant.
10149  *
10150  * Fork - LGPL
10151  * <script type="text/javascript">
10152  */
10153
10154 /**
10155 /**
10156  * @extends Roo.data.Store
10157  * @class Roo.data.JsonStore
10158  * Small helper class to make creating Stores for JSON data easier. <br/>
10159 <pre><code>
10160 var store = new Roo.data.JsonStore({
10161     url: 'get-images.php',
10162     root: 'images',
10163     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10164 });
10165 </code></pre>
10166  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10167  * JsonReader and HttpProxy (unless inline data is provided).</b>
10168  * @cfg {Array} fields An array of field definition objects, or field name strings.
10169  * @constructor
10170  * @param {Object} config
10171  */
10172 Roo.data.JsonStore = function(c){
10173     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10174         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10175         reader: new Roo.data.JsonReader(c, c.fields)
10176     }));
10177 };
10178 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10179  * Based on:
10180  * Ext JS Library 1.1.1
10181  * Copyright(c) 2006-2007, Ext JS, LLC.
10182  *
10183  * Originally Released Under LGPL - original licence link has changed is not relivant.
10184  *
10185  * Fork - LGPL
10186  * <script type="text/javascript">
10187  */
10188
10189  
10190 Roo.data.Field = function(config){
10191     if(typeof config == "string"){
10192         config = {name: config};
10193     }
10194     Roo.apply(this, config);
10195     
10196     if(!this.type){
10197         this.type = "auto";
10198     }
10199     
10200     var st = Roo.data.SortTypes;
10201     // named sortTypes are supported, here we look them up
10202     if(typeof this.sortType == "string"){
10203         this.sortType = st[this.sortType];
10204     }
10205     
10206     // set default sortType for strings and dates
10207     if(!this.sortType){
10208         switch(this.type){
10209             case "string":
10210                 this.sortType = st.asUCString;
10211                 break;
10212             case "date":
10213                 this.sortType = st.asDate;
10214                 break;
10215             default:
10216                 this.sortType = st.none;
10217         }
10218     }
10219
10220     // define once
10221     var stripRe = /[\$,%]/g;
10222
10223     // prebuilt conversion function for this field, instead of
10224     // switching every time we're reading a value
10225     if(!this.convert){
10226         var cv, dateFormat = this.dateFormat;
10227         switch(this.type){
10228             case "":
10229             case "auto":
10230             case undefined:
10231                 cv = function(v){ return v; };
10232                 break;
10233             case "string":
10234                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10235                 break;
10236             case "int":
10237                 cv = function(v){
10238                     return v !== undefined && v !== null && v !== '' ?
10239                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10240                     };
10241                 break;
10242             case "float":
10243                 cv = function(v){
10244                     return v !== undefined && v !== null && v !== '' ?
10245                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10246                     };
10247                 break;
10248             case "bool":
10249             case "boolean":
10250                 cv = function(v){ return v === true || v === "true" || v == 1; };
10251                 break;
10252             case "date":
10253                 cv = function(v){
10254                     if(!v){
10255                         return '';
10256                     }
10257                     if(v instanceof Date){
10258                         return v;
10259                     }
10260                     if(dateFormat){
10261                         if(dateFormat == "timestamp"){
10262                             return new Date(v*1000);
10263                         }
10264                         return Date.parseDate(v, dateFormat);
10265                     }
10266                     var parsed = Date.parse(v);
10267                     return parsed ? new Date(parsed) : null;
10268                 };
10269              break;
10270             
10271         }
10272         this.convert = cv;
10273     }
10274 };
10275
10276 Roo.data.Field.prototype = {
10277     dateFormat: null,
10278     defaultValue: "",
10279     mapping: null,
10280     sortType : null,
10281     sortDir : "ASC"
10282 };/*
10283  * Based on:
10284  * Ext JS Library 1.1.1
10285  * Copyright(c) 2006-2007, Ext JS, LLC.
10286  *
10287  * Originally Released Under LGPL - original licence link has changed is not relivant.
10288  *
10289  * Fork - LGPL
10290  * <script type="text/javascript">
10291  */
10292  
10293 // Base class for reading structured data from a data source.  This class is intended to be
10294 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10295
10296 /**
10297  * @class Roo.data.DataReader
10298  * Base class for reading structured data from a data source.  This class is intended to be
10299  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10300  */
10301
10302 Roo.data.DataReader = function(meta, recordType){
10303     
10304     this.meta = meta;
10305     
10306     this.recordType = recordType instanceof Array ? 
10307         Roo.data.Record.create(recordType) : recordType;
10308 };
10309
10310 Roo.data.DataReader.prototype = {
10311      /**
10312      * Create an empty record
10313      * @param {Object} data (optional) - overlay some values
10314      * @return {Roo.data.Record} record created.
10315      */
10316     newRow :  function(d) {
10317         var da =  {};
10318         this.recordType.prototype.fields.each(function(c) {
10319             switch( c.type) {
10320                 case 'int' : da[c.name] = 0; break;
10321                 case 'date' : da[c.name] = new Date(); break;
10322                 case 'float' : da[c.name] = 0.0; break;
10323                 case 'boolean' : da[c.name] = false; break;
10324                 default : da[c.name] = ""; break;
10325             }
10326             
10327         });
10328         return new this.recordType(Roo.apply(da, d));
10329     }
10330     
10331 };/*
10332  * Based on:
10333  * Ext JS Library 1.1.1
10334  * Copyright(c) 2006-2007, Ext JS, LLC.
10335  *
10336  * Originally Released Under LGPL - original licence link has changed is not relivant.
10337  *
10338  * Fork - LGPL
10339  * <script type="text/javascript">
10340  */
10341
10342 /**
10343  * @class Roo.data.DataProxy
10344  * @extends Roo.data.Observable
10345  * This class is an abstract base class for implementations which provide retrieval of
10346  * unformatted data objects.<br>
10347  * <p>
10348  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10349  * (of the appropriate type which knows how to parse the data object) to provide a block of
10350  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10351  * <p>
10352  * Custom implementations must implement the load method as described in
10353  * {@link Roo.data.HttpProxy#load}.
10354  */
10355 Roo.data.DataProxy = function(){
10356     this.addEvents({
10357         /**
10358          * @event beforeload
10359          * Fires before a network request is made to retrieve a data object.
10360          * @param {Object} This DataProxy object.
10361          * @param {Object} params The params parameter to the load function.
10362          */
10363         beforeload : true,
10364         /**
10365          * @event load
10366          * Fires before the load method's callback is called.
10367          * @param {Object} This DataProxy object.
10368          * @param {Object} o The data object.
10369          * @param {Object} arg The callback argument object passed to the load function.
10370          */
10371         load : true,
10372         /**
10373          * @event loadexception
10374          * Fires if an Exception occurs during data retrieval.
10375          * @param {Object} This DataProxy object.
10376          * @param {Object} o The data object.
10377          * @param {Object} arg The callback argument object passed to the load function.
10378          * @param {Object} e The Exception.
10379          */
10380         loadexception : true
10381     });
10382     Roo.data.DataProxy.superclass.constructor.call(this);
10383 };
10384
10385 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10386
10387     /**
10388      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10389      */
10390 /*
10391  * Based on:
10392  * Ext JS Library 1.1.1
10393  * Copyright(c) 2006-2007, Ext JS, LLC.
10394  *
10395  * Originally Released Under LGPL - original licence link has changed is not relivant.
10396  *
10397  * Fork - LGPL
10398  * <script type="text/javascript">
10399  */
10400 /**
10401  * @class Roo.data.MemoryProxy
10402  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10403  * to the Reader when its load method is called.
10404  * @constructor
10405  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10406  */
10407 Roo.data.MemoryProxy = function(data){
10408     if (data.data) {
10409         data = data.data;
10410     }
10411     Roo.data.MemoryProxy.superclass.constructor.call(this);
10412     this.data = data;
10413 };
10414
10415 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10416     /**
10417      * Load data from the requested source (in this case an in-memory
10418      * data object passed to the constructor), read the data object into
10419      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10420      * process that block using the passed callback.
10421      * @param {Object} params This parameter is not used by the MemoryProxy class.
10422      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10423      * object into a block of Roo.data.Records.
10424      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10425      * The function must be passed <ul>
10426      * <li>The Record block object</li>
10427      * <li>The "arg" argument from the load function</li>
10428      * <li>A boolean success indicator</li>
10429      * </ul>
10430      * @param {Object} scope The scope in which to call the callback
10431      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10432      */
10433     load : function(params, reader, callback, scope, arg){
10434         params = params || {};
10435         var result;
10436         try {
10437             result = reader.readRecords(this.data);
10438         }catch(e){
10439             this.fireEvent("loadexception", this, arg, null, e);
10440             callback.call(scope, null, arg, false);
10441             return;
10442         }
10443         callback.call(scope, result, arg, true);
10444     },
10445     
10446     // private
10447     update : function(params, records){
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  * @class Roo.data.HttpProxy
10462  * @extends Roo.data.DataProxy
10463  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10464  * configured to reference a certain URL.<br><br>
10465  * <p>
10466  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10467  * from which the running page was served.<br><br>
10468  * <p>
10469  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10470  * <p>
10471  * Be aware that to enable the browser to parse an XML document, the server must set
10472  * the Content-Type header in the HTTP response to "text/xml".
10473  * @constructor
10474  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10475  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10476  * will be used to make the request.
10477  */
10478 Roo.data.HttpProxy = function(conn){
10479     Roo.data.HttpProxy.superclass.constructor.call(this);
10480     // is conn a conn config or a real conn?
10481     this.conn = conn;
10482     this.useAjax = !conn || !conn.events;
10483   
10484 };
10485
10486 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10487     // thse are take from connection...
10488     
10489     /**
10490      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10491      */
10492     /**
10493      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10494      * extra parameters to each request made by this object. (defaults to undefined)
10495      */
10496     /**
10497      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10498      *  to each request made by this object. (defaults to undefined)
10499      */
10500     /**
10501      * @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)
10502      */
10503     /**
10504      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10505      */
10506      /**
10507      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10508      * @type Boolean
10509      */
10510   
10511
10512     /**
10513      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10514      * @type Boolean
10515      */
10516     /**
10517      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10518      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10519      * a finer-grained basis than the DataProxy events.
10520      */
10521     getConnection : function(){
10522         return this.useAjax ? Roo.Ajax : this.conn;
10523     },
10524
10525     /**
10526      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10527      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10528      * process that block using the passed callback.
10529      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10530      * for the request to the remote server.
10531      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10532      * object into a block of Roo.data.Records.
10533      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10534      * The function must be passed <ul>
10535      * <li>The Record block object</li>
10536      * <li>The "arg" argument from the load function</li>
10537      * <li>A boolean success indicator</li>
10538      * </ul>
10539      * @param {Object} scope The scope in which to call the callback
10540      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10541      */
10542     load : function(params, reader, callback, scope, arg){
10543         if(this.fireEvent("beforeload", this, params) !== false){
10544             var  o = {
10545                 params : params || {},
10546                 request: {
10547                     callback : callback,
10548                     scope : scope,
10549                     arg : arg
10550                 },
10551                 reader: reader,
10552                 callback : this.loadResponse,
10553                 scope: this
10554             };
10555             if(this.useAjax){
10556                 Roo.applyIf(o, this.conn);
10557                 if(this.activeRequest){
10558                     Roo.Ajax.abort(this.activeRequest);
10559                 }
10560                 this.activeRequest = Roo.Ajax.request(o);
10561             }else{
10562                 this.conn.request(o);
10563             }
10564         }else{
10565             callback.call(scope||this, null, arg, false);
10566         }
10567     },
10568
10569     // private
10570     loadResponse : function(o, success, response){
10571         delete this.activeRequest;
10572         if(!success){
10573             this.fireEvent("loadexception", this, o, response);
10574             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10575             return;
10576         }
10577         var result;
10578         try {
10579             result = o.reader.read(response);
10580         }catch(e){
10581             this.fireEvent("loadexception", this, o, response, e);
10582             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10583             return;
10584         }
10585         
10586         this.fireEvent("load", this, o, o.request.arg);
10587         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10588     },
10589
10590     // private
10591     update : function(dataSet){
10592
10593     },
10594
10595     // private
10596     updateResponse : function(dataSet){
10597
10598     }
10599 });/*
10600  * Based on:
10601  * Ext JS Library 1.1.1
10602  * Copyright(c) 2006-2007, Ext JS, LLC.
10603  *
10604  * Originally Released Under LGPL - original licence link has changed is not relivant.
10605  *
10606  * Fork - LGPL
10607  * <script type="text/javascript">
10608  */
10609
10610 /**
10611  * @class Roo.data.ScriptTagProxy
10612  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10613  * other than the originating domain of the running page.<br><br>
10614  * <p>
10615  * <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
10616  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10617  * <p>
10618  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10619  * source code that is used as the source inside a &lt;script> tag.<br><br>
10620  * <p>
10621  * In order for the browser to process the returned data, the server must wrap the data object
10622  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10623  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10624  * depending on whether the callback name was passed:
10625  * <p>
10626  * <pre><code>
10627 boolean scriptTag = false;
10628 String cb = request.getParameter("callback");
10629 if (cb != null) {
10630     scriptTag = true;
10631     response.setContentType("text/javascript");
10632 } else {
10633     response.setContentType("application/x-json");
10634 }
10635 Writer out = response.getWriter();
10636 if (scriptTag) {
10637     out.write(cb + "(");
10638 }
10639 out.print(dataBlock.toJsonString());
10640 if (scriptTag) {
10641     out.write(");");
10642 }
10643 </pre></code>
10644  *
10645  * @constructor
10646  * @param {Object} config A configuration object.
10647  */
10648 Roo.data.ScriptTagProxy = function(config){
10649     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10650     Roo.apply(this, config);
10651     this.head = document.getElementsByTagName("head")[0];
10652 };
10653
10654 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10655
10656 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10657     /**
10658      * @cfg {String} url The URL from which to request the data object.
10659      */
10660     /**
10661      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10662      */
10663     timeout : 30000,
10664     /**
10665      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10666      * the server the name of the callback function set up by the load call to process the returned data object.
10667      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10668      * javascript output which calls this named function passing the data object as its only parameter.
10669      */
10670     callbackParam : "callback",
10671     /**
10672      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10673      * name to the request.
10674      */
10675     nocache : true,
10676
10677     /**
10678      * Load data from the configured URL, read the data object into
10679      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10680      * process that block using the passed callback.
10681      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10682      * for the request to the remote server.
10683      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10684      * object into a block of Roo.data.Records.
10685      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10686      * The function must be passed <ul>
10687      * <li>The Record block object</li>
10688      * <li>The "arg" argument from the load function</li>
10689      * <li>A boolean success indicator</li>
10690      * </ul>
10691      * @param {Object} scope The scope in which to call the callback
10692      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10693      */
10694     load : function(params, reader, callback, scope, arg){
10695         if(this.fireEvent("beforeload", this, params) !== false){
10696
10697             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10698
10699             var url = this.url;
10700             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10701             if(this.nocache){
10702                 url += "&_dc=" + (new Date().getTime());
10703             }
10704             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10705             var trans = {
10706                 id : transId,
10707                 cb : "stcCallback"+transId,
10708                 scriptId : "stcScript"+transId,
10709                 params : params,
10710                 arg : arg,
10711                 url : url,
10712                 callback : callback,
10713                 scope : scope,
10714                 reader : reader
10715             };
10716             var conn = this;
10717
10718             window[trans.cb] = function(o){
10719                 conn.handleResponse(o, trans);
10720             };
10721
10722             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10723
10724             if(this.autoAbort !== false){
10725                 this.abort();
10726             }
10727
10728             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10729
10730             var script = document.createElement("script");
10731             script.setAttribute("src", url);
10732             script.setAttribute("type", "text/javascript");
10733             script.setAttribute("id", trans.scriptId);
10734             this.head.appendChild(script);
10735
10736             this.trans = trans;
10737         }else{
10738             callback.call(scope||this, null, arg, false);
10739         }
10740     },
10741
10742     // private
10743     isLoading : function(){
10744         return this.trans ? true : false;
10745     },
10746
10747     /**
10748      * Abort the current server request.
10749      */
10750     abort : function(){
10751         if(this.isLoading()){
10752             this.destroyTrans(this.trans);
10753         }
10754     },
10755
10756     // private
10757     destroyTrans : function(trans, isLoaded){
10758         this.head.removeChild(document.getElementById(trans.scriptId));
10759         clearTimeout(trans.timeoutId);
10760         if(isLoaded){
10761             window[trans.cb] = undefined;
10762             try{
10763                 delete window[trans.cb];
10764             }catch(e){}
10765         }else{
10766             // if hasn't been loaded, wait for load to remove it to prevent script error
10767             window[trans.cb] = function(){
10768                 window[trans.cb] = undefined;
10769                 try{
10770                     delete window[trans.cb];
10771                 }catch(e){}
10772             };
10773         }
10774     },
10775
10776     // private
10777     handleResponse : function(o, trans){
10778         this.trans = false;
10779         this.destroyTrans(trans, true);
10780         var result;
10781         try {
10782             result = trans.reader.readRecords(o);
10783         }catch(e){
10784             this.fireEvent("loadexception", this, o, trans.arg, e);
10785             trans.callback.call(trans.scope||window, null, trans.arg, false);
10786             return;
10787         }
10788         this.fireEvent("load", this, o, trans.arg);
10789         trans.callback.call(trans.scope||window, result, trans.arg, true);
10790     },
10791
10792     // private
10793     handleFailure : function(trans){
10794         this.trans = false;
10795         this.destroyTrans(trans, false);
10796         this.fireEvent("loadexception", this, null, trans.arg);
10797         trans.callback.call(trans.scope||window, null, trans.arg, false);
10798     }
10799 });/*
10800  * Based on:
10801  * Ext JS Library 1.1.1
10802  * Copyright(c) 2006-2007, Ext JS, LLC.
10803  *
10804  * Originally Released Under LGPL - original licence link has changed is not relivant.
10805  *
10806  * Fork - LGPL
10807  * <script type="text/javascript">
10808  */
10809
10810 /**
10811  * @class Roo.data.JsonReader
10812  * @extends Roo.data.DataReader
10813  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10814  * based on mappings in a provided Roo.data.Record constructor.
10815  * 
10816  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10817  * in the reply previously. 
10818  * 
10819  * <p>
10820  * Example code:
10821  * <pre><code>
10822 var RecordDef = Roo.data.Record.create([
10823     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10824     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10825 ]);
10826 var myReader = new Roo.data.JsonReader({
10827     totalProperty: "results",    // The property which contains the total dataset size (optional)
10828     root: "rows",                // The property which contains an Array of row objects
10829     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10830 }, RecordDef);
10831 </code></pre>
10832  * <p>
10833  * This would consume a JSON file like this:
10834  * <pre><code>
10835 { 'results': 2, 'rows': [
10836     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10837     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10838 }
10839 </code></pre>
10840  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10841  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10842  * paged from the remote server.
10843  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10844  * @cfg {String} root name of the property which contains the Array of row objects.
10845  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10846  * @cfg {Array} fields Array of field definition objects
10847  * @constructor
10848  * Create a new JsonReader
10849  * @param {Object} meta Metadata configuration options
10850  * @param {Object} recordType Either an Array of field definition objects,
10851  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10852  */
10853 Roo.data.JsonReader = function(meta, recordType){
10854     
10855     meta = meta || {};
10856     // set some defaults:
10857     Roo.applyIf(meta, {
10858         totalProperty: 'total',
10859         successProperty : 'success',
10860         root : 'data',
10861         id : 'id'
10862     });
10863     
10864     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10865 };
10866 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10867     
10868     /**
10869      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10870      * Used by Store query builder to append _requestMeta to params.
10871      * 
10872      */
10873     metaFromRemote : false,
10874     /**
10875      * This method is only used by a DataProxy which has retrieved data from a remote server.
10876      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10877      * @return {Object} data A data block which is used by an Roo.data.Store object as
10878      * a cache of Roo.data.Records.
10879      */
10880     read : function(response){
10881         var json = response.responseText;
10882        
10883         var o = /* eval:var:o */ eval("("+json+")");
10884         if(!o) {
10885             throw {message: "JsonReader.read: Json object not found"};
10886         }
10887         
10888         if(o.metaData){
10889             
10890             delete this.ef;
10891             this.metaFromRemote = true;
10892             this.meta = o.metaData;
10893             this.recordType = Roo.data.Record.create(o.metaData.fields);
10894             this.onMetaChange(this.meta, this.recordType, o);
10895         }
10896         return this.readRecords(o);
10897     },
10898
10899     // private function a store will implement
10900     onMetaChange : function(meta, recordType, o){
10901
10902     },
10903
10904     /**
10905          * @ignore
10906          */
10907     simpleAccess: function(obj, subsc) {
10908         return obj[subsc];
10909     },
10910
10911         /**
10912          * @ignore
10913          */
10914     getJsonAccessor: function(){
10915         var re = /[\[\.]/;
10916         return function(expr) {
10917             try {
10918                 return(re.test(expr))
10919                     ? new Function("obj", "return obj." + expr)
10920                     : function(obj){
10921                         return obj[expr];
10922                     };
10923             } catch(e){}
10924             return Roo.emptyFn;
10925         };
10926     }(),
10927
10928     /**
10929      * Create a data block containing Roo.data.Records from an XML document.
10930      * @param {Object} o An object which contains an Array of row objects in the property specified
10931      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10932      * which contains the total size of the dataset.
10933      * @return {Object} data A data block which is used by an Roo.data.Store object as
10934      * a cache of Roo.data.Records.
10935      */
10936     readRecords : function(o){
10937         /**
10938          * After any data loads, the raw JSON data is available for further custom processing.
10939          * @type Object
10940          */
10941         this.o = o;
10942         var s = this.meta, Record = this.recordType,
10943             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10944
10945 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10946         if (!this.ef) {
10947             if(s.totalProperty) {
10948                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10949                 }
10950                 if(s.successProperty) {
10951                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10952                 }
10953                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10954                 if (s.id) {
10955                         var g = this.getJsonAccessor(s.id);
10956                         this.getId = function(rec) {
10957                                 var r = g(rec);  
10958                                 return (r === undefined || r === "") ? null : r;
10959                         };
10960                 } else {
10961                         this.getId = function(){return null;};
10962                 }
10963             this.ef = [];
10964             for(var jj = 0; jj < fl; jj++){
10965                 f = fi[jj];
10966                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10967                 this.ef[jj] = this.getJsonAccessor(map);
10968             }
10969         }
10970
10971         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10972         if(s.totalProperty){
10973             var vt = parseInt(this.getTotal(o), 10);
10974             if(!isNaN(vt)){
10975                 totalRecords = vt;
10976             }
10977         }
10978         if(s.successProperty){
10979             var vs = this.getSuccess(o);
10980             if(vs === false || vs === 'false'){
10981                 success = false;
10982             }
10983         }
10984         var records = [];
10985         for(var i = 0; i < c; i++){
10986                 var n = root[i];
10987             var values = {};
10988             var id = this.getId(n);
10989             for(var j = 0; j < fl; j++){
10990                 f = fi[j];
10991             var v = this.ef[j](n);
10992             if (!f.convert) {
10993                 Roo.log('missing convert for ' + f.name);
10994                 Roo.log(f);
10995                 continue;
10996             }
10997             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10998             }
10999             var record = new Record(values, id);
11000             record.json = n;
11001             records[i] = record;
11002         }
11003         return {
11004             raw : o,
11005             success : success,
11006             records : records,
11007             totalRecords : totalRecords
11008         };
11009     }
11010 });/*
11011  * Based on:
11012  * Ext JS Library 1.1.1
11013  * Copyright(c) 2006-2007, Ext JS, LLC.
11014  *
11015  * Originally Released Under LGPL - original licence link has changed is not relivant.
11016  *
11017  * Fork - LGPL
11018  * <script type="text/javascript">
11019  */
11020
11021 /**
11022  * @class Roo.data.ArrayReader
11023  * @extends Roo.data.DataReader
11024  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11025  * Each element of that Array represents a row of data fields. The
11026  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11027  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11028  * <p>
11029  * Example code:.
11030  * <pre><code>
11031 var RecordDef = Roo.data.Record.create([
11032     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11033     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11034 ]);
11035 var myReader = new Roo.data.ArrayReader({
11036     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11037 }, RecordDef);
11038 </code></pre>
11039  * <p>
11040  * This would consume an Array like this:
11041  * <pre><code>
11042 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11043   </code></pre>
11044  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11045  * @constructor
11046  * Create a new JsonReader
11047  * @param {Object} meta Metadata configuration options.
11048  * @param {Object} recordType Either an Array of field definition objects
11049  * as specified to {@link Roo.data.Record#create},
11050  * or an {@link Roo.data.Record} object
11051  * created using {@link Roo.data.Record#create}.
11052  */
11053 Roo.data.ArrayReader = function(meta, recordType){
11054     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11055 };
11056
11057 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11058     /**
11059      * Create a data block containing Roo.data.Records from an XML document.
11060      * @param {Object} o An Array of row objects which represents the dataset.
11061      * @return {Object} data A data block which is used by an Roo.data.Store object as
11062      * a cache of Roo.data.Records.
11063      */
11064     readRecords : function(o){
11065         var sid = this.meta ? this.meta.id : null;
11066         var recordType = this.recordType, fields = recordType.prototype.fields;
11067         var records = [];
11068         var root = o;
11069             for(var i = 0; i < root.length; i++){
11070                     var n = root[i];
11071                 var values = {};
11072                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11073                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11074                 var f = fields.items[j];
11075                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11076                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11077                 v = f.convert(v);
11078                 values[f.name] = v;
11079             }
11080                 var record = new recordType(values, id);
11081                 record.json = n;
11082                 records[records.length] = record;
11083             }
11084             return {
11085                 records : records,
11086                 totalRecords : records.length
11087             };
11088     }
11089 });/*
11090  * - LGPL
11091  * * 
11092  */
11093
11094 /**
11095  * @class Roo.bootstrap.ComboBox
11096  * @extends Roo.bootstrap.TriggerField
11097  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11098  * @cfg {Boolean} append (true|false) default false
11099  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11100  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11101  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11102  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11103  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11104  * @cfg {Boolean} animate default true
11105  * @cfg {Boolean} emptyResultText only for touch device
11106  * @constructor
11107  * Create a new ComboBox.
11108  * @param {Object} config Configuration options
11109  */
11110 Roo.bootstrap.ComboBox = function(config){
11111     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11112     this.addEvents({
11113         /**
11114          * @event expand
11115          * Fires when the dropdown list is expanded
11116              * @param {Roo.bootstrap.ComboBox} combo This combo box
11117              */
11118         'expand' : true,
11119         /**
11120          * @event collapse
11121          * Fires when the dropdown list is collapsed
11122              * @param {Roo.bootstrap.ComboBox} combo This combo box
11123              */
11124         'collapse' : true,
11125         /**
11126          * @event beforeselect
11127          * Fires before a list item is selected. Return false to cancel the selection.
11128              * @param {Roo.bootstrap.ComboBox} combo This combo box
11129              * @param {Roo.data.Record} record The data record returned from the underlying store
11130              * @param {Number} index The index of the selected item in the dropdown list
11131              */
11132         'beforeselect' : true,
11133         /**
11134          * @event select
11135          * Fires when a list item is selected
11136              * @param {Roo.bootstrap.ComboBox} combo This combo box
11137              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11138              * @param {Number} index The index of the selected item in the dropdown list
11139              */
11140         'select' : true,
11141         /**
11142          * @event beforequery
11143          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11144          * The event object passed has these properties:
11145              * @param {Roo.bootstrap.ComboBox} combo This combo box
11146              * @param {String} query The query
11147              * @param {Boolean} forceAll true to force "all" query
11148              * @param {Boolean} cancel true to cancel the query
11149              * @param {Object} e The query event object
11150              */
11151         'beforequery': true,
11152          /**
11153          * @event add
11154          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11155              * @param {Roo.bootstrap.ComboBox} combo This combo box
11156              */
11157         'add' : true,
11158         /**
11159          * @event edit
11160          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11161              * @param {Roo.bootstrap.ComboBox} combo This combo box
11162              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11163              */
11164         'edit' : true,
11165         /**
11166          * @event remove
11167          * Fires when the remove value from the combobox array
11168              * @param {Roo.bootstrap.ComboBox} combo This combo box
11169              */
11170         'remove' : true,
11171         /**
11172          * @event specialfilter
11173          * Fires when specialfilter
11174             * @param {Roo.bootstrap.ComboBox} combo This combo box
11175             */
11176         'specialfilter' : true
11177         
11178     });
11179     
11180     this.item = [];
11181     this.tickItems = [];
11182     
11183     this.selectedIndex = -1;
11184     if(this.mode == 'local'){
11185         if(config.queryDelay === undefined){
11186             this.queryDelay = 10;
11187         }
11188         if(config.minChars === undefined){
11189             this.minChars = 0;
11190         }
11191     }
11192 };
11193
11194 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11195      
11196     /**
11197      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11198      * rendering into an Roo.Editor, defaults to false)
11199      */
11200     /**
11201      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11202      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11203      */
11204     /**
11205      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11209      * the dropdown list (defaults to undefined, with no header element)
11210      */
11211
11212      /**
11213      * @cfg {String/Roo.Template} tpl The template to use to render the output
11214      */
11215      
11216      /**
11217      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11218      */
11219     listWidth: undefined,
11220     /**
11221      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11222      * mode = 'remote' or 'text' if mode = 'local')
11223      */
11224     displayField: undefined,
11225     
11226     /**
11227      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11228      * mode = 'remote' or 'value' if mode = 'local'). 
11229      * Note: use of a valueField requires the user make a selection
11230      * in order for a value to be mapped.
11231      */
11232     valueField: undefined,
11233     
11234     
11235     /**
11236      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11237      * field's data value (defaults to the underlying DOM element's name)
11238      */
11239     hiddenName: undefined,
11240     /**
11241      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11242      */
11243     listClass: '',
11244     /**
11245      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11246      */
11247     selectedClass: 'active',
11248     
11249     /**
11250      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11251      */
11252     shadow:'sides',
11253     /**
11254      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11255      * anchor positions (defaults to 'tl-bl')
11256      */
11257     listAlign: 'tl-bl?',
11258     /**
11259      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11260      */
11261     maxHeight: 300,
11262     /**
11263      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11264      * query specified by the allQuery config option (defaults to 'query')
11265      */
11266     triggerAction: 'query',
11267     /**
11268      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11269      * (defaults to 4, does not apply if editable = false)
11270      */
11271     minChars : 4,
11272     /**
11273      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11274      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11275      */
11276     typeAhead: false,
11277     /**
11278      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11279      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11280      */
11281     queryDelay: 500,
11282     /**
11283      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11284      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11285      */
11286     pageSize: 0,
11287     /**
11288      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11289      * when editable = true (defaults to false)
11290      */
11291     selectOnFocus:false,
11292     /**
11293      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11294      */
11295     queryParam: 'query',
11296     /**
11297      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11298      * when mode = 'remote' (defaults to 'Loading...')
11299      */
11300     loadingText: 'Loading...',
11301     /**
11302      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11303      */
11304     resizable: false,
11305     /**
11306      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11307      */
11308     handleHeight : 8,
11309     /**
11310      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11311      * traditional select (defaults to true)
11312      */
11313     editable: true,
11314     /**
11315      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11316      */
11317     allQuery: '',
11318     /**
11319      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11320      */
11321     mode: 'remote',
11322     /**
11323      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11324      * listWidth has a higher value)
11325      */
11326     minListWidth : 70,
11327     /**
11328      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11329      * allow the user to set arbitrary text into the field (defaults to false)
11330      */
11331     forceSelection:false,
11332     /**
11333      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11334      * if typeAhead = true (defaults to 250)
11335      */
11336     typeAheadDelay : 250,
11337     /**
11338      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11339      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11340      */
11341     valueNotFoundText : undefined,
11342     /**
11343      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11344      */
11345     blockFocus : false,
11346     
11347     /**
11348      * @cfg {Boolean} disableClear Disable showing of clear button.
11349      */
11350     disableClear : false,
11351     /**
11352      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11353      */
11354     alwaysQuery : false,
11355     
11356     /**
11357      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11358      */
11359     multiple : false,
11360     
11361     /**
11362      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11363      */
11364     invalidClass : "has-warning",
11365     
11366     /**
11367      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11368      */
11369     validClass : "has-success",
11370     
11371     /**
11372      * @cfg {Boolean} specialFilter (true|false) special filter default false
11373      */
11374     specialFilter : false,
11375     
11376     /**
11377      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11378      */
11379     mobileTouchView : true,
11380     
11381     //private
11382     addicon : false,
11383     editicon: false,
11384     
11385     page: 0,
11386     hasQuery: false,
11387     append: false,
11388     loadNext: false,
11389     autoFocus : true,
11390     tickable : false,
11391     btnPosition : 'right',
11392     triggerList : true,
11393     showToggleBtn : true,
11394     animate : true,
11395     emptyResultText: 'Empty',
11396     // element that contains real text value.. (when hidden is used..)
11397     
11398     getAutoCreate : function()
11399     {
11400         var cfg = false;
11401         
11402         /*
11403          * Touch Devices
11404          */
11405         
11406         if(Roo.isTouch && this.mobileTouchView){
11407             cfg = this.getAutoCreateTouchView();
11408             return cfg;;
11409         }
11410         
11411         /*
11412          *  Normal ComboBox
11413          */
11414         if(!this.tickable){
11415             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11416             return cfg;
11417         }
11418         
11419         /*
11420          *  ComboBox with tickable selections
11421          */
11422              
11423         var align = this.labelAlign || this.parentLabelAlign();
11424         
11425         cfg = {
11426             cls : 'form-group roo-combobox-tickable' //input-group
11427         };
11428         
11429         var buttons = {
11430             tag : 'div',
11431             cls : 'tickable-buttons',
11432             cn : [
11433                 {
11434                     tag : 'button',
11435                     type : 'button',
11436                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11437                     html : 'Edit'
11438                 },
11439                 {
11440                     tag : 'button',
11441                     type : 'button',
11442                     name : 'ok',
11443                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11444                     html : 'Done'
11445                 },
11446                 {
11447                     tag : 'button',
11448                     type : 'button',
11449                     name : 'cancel',
11450                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11451                     html : 'Cancel'
11452                 }
11453             ]
11454         };
11455         
11456         if(this.editable){
11457             buttons.cn.unshift({
11458                 tag: 'input',
11459                 cls: 'select2-search-field-input'
11460             });
11461         }
11462         
11463         var _this = this;
11464         
11465         Roo.each(buttons.cn, function(c){
11466             if (_this.size) {
11467                 c.cls += ' btn-' + _this.size;
11468             }
11469
11470             if (_this.disabled) {
11471                 c.disabled = true;
11472             }
11473         });
11474         
11475         var box = {
11476             tag: 'div',
11477             cn: [
11478                 {
11479                     tag: 'input',
11480                     type : 'hidden',
11481                     cls: 'form-hidden-field'
11482                 },
11483                 {
11484                     tag: 'ul',
11485                     cls: 'select2-choices',
11486                     cn:[
11487                         {
11488                             tag: 'li',
11489                             cls: 'select2-search-field',
11490                             cn: [
11491
11492                                 buttons
11493                             ]
11494                         }
11495                     ]
11496                 }
11497             ]
11498         }
11499         
11500         var combobox = {
11501             cls: 'select2-container input-group select2-container-multi',
11502             cn: [
11503                 box
11504 //                {
11505 //                    tag: 'ul',
11506 //                    cls: 'typeahead typeahead-long dropdown-menu',
11507 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11508 //                }
11509             ]
11510         };
11511         
11512         if(this.hasFeedback && !this.allowBlank){
11513             
11514             var feedback = {
11515                 tag: 'span',
11516                 cls: 'glyphicon form-control-feedback'
11517             };
11518
11519             combobox.cn.push(feedback);
11520         }
11521         
11522         if (align ==='left' && this.fieldLabel.length) {
11523             
11524                 Roo.log("left and has label");
11525                 cfg.cn = [
11526                     
11527                     {
11528                         tag: 'label',
11529                         'for' :  id,
11530                         cls : 'control-label col-sm-' + this.labelWidth,
11531                         html : this.fieldLabel
11532                         
11533                     },
11534                     {
11535                         cls : "col-sm-" + (12 - this.labelWidth), 
11536                         cn: [
11537                             combobox
11538                         ]
11539                     }
11540                     
11541                 ];
11542         } else if ( this.fieldLabel.length) {
11543                 Roo.log(" label");
11544                  cfg.cn = [
11545                    
11546                     {
11547                         tag: 'label',
11548                         //cls : 'input-group-addon',
11549                         html : this.fieldLabel
11550                         
11551                     },
11552                     
11553                     combobox
11554                     
11555                 ];
11556
11557         } else {
11558             
11559                 Roo.log(" no label && no align");
11560                 cfg = combobox
11561                      
11562                 
11563         }
11564          
11565         var settings=this;
11566         ['xs','sm','md','lg'].map(function(size){
11567             if (settings[size]) {
11568                 cfg.cls += ' col-' + size + '-' + settings[size];
11569             }
11570         });
11571         
11572         return cfg;
11573         
11574     },
11575     
11576     // private
11577     initEvents: function()
11578     {
11579         
11580         if (!this.store) {
11581             throw "can not find store for combo";
11582         }
11583         
11584         this.store = Roo.factory(this.store, Roo.data);
11585         
11586         /*
11587          * Touch Devices
11588          */
11589         
11590         if(Roo.isTouch && this.mobileTouchView){
11591             this.initTouchView();
11592             return;
11593         }
11594         
11595         if(this.tickable){
11596             this.initTickableEvents();
11597             return;
11598         }
11599         
11600         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11601         
11602         if(this.hiddenName){
11603             
11604             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11605             
11606             this.hiddenField.dom.value =
11607                 this.hiddenValue !== undefined ? this.hiddenValue :
11608                 this.value !== undefined ? this.value : '';
11609
11610             // prevent input submission
11611             this.el.dom.removeAttribute('name');
11612             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11613              
11614              
11615         }
11616         //if(Roo.isGecko){
11617         //    this.el.dom.setAttribute('autocomplete', 'off');
11618         //}
11619         
11620         var cls = 'x-combo-list';
11621         
11622         //this.list = new Roo.Layer({
11623         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11624         //});
11625         
11626         var _this = this;
11627         
11628         (function(){
11629             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11630             _this.list.setWidth(lw);
11631         }).defer(100);
11632         
11633         this.list.on('mouseover', this.onViewOver, this);
11634         this.list.on('mousemove', this.onViewMove, this);
11635         
11636         this.list.on('scroll', this.onViewScroll, this);
11637         
11638         /*
11639         this.list.swallowEvent('mousewheel');
11640         this.assetHeight = 0;
11641
11642         if(this.title){
11643             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11644             this.assetHeight += this.header.getHeight();
11645         }
11646
11647         this.innerList = this.list.createChild({cls:cls+'-inner'});
11648         this.innerList.on('mouseover', this.onViewOver, this);
11649         this.innerList.on('mousemove', this.onViewMove, this);
11650         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11651         
11652         if(this.allowBlank && !this.pageSize && !this.disableClear){
11653             this.footer = this.list.createChild({cls:cls+'-ft'});
11654             this.pageTb = new Roo.Toolbar(this.footer);
11655            
11656         }
11657         if(this.pageSize){
11658             this.footer = this.list.createChild({cls:cls+'-ft'});
11659             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11660                     {pageSize: this.pageSize});
11661             
11662         }
11663         
11664         if (this.pageTb && this.allowBlank && !this.disableClear) {
11665             var _this = this;
11666             this.pageTb.add(new Roo.Toolbar.Fill(), {
11667                 cls: 'x-btn-icon x-btn-clear',
11668                 text: '&#160;',
11669                 handler: function()
11670                 {
11671                     _this.collapse();
11672                     _this.clearValue();
11673                     _this.onSelect(false, -1);
11674                 }
11675             });
11676         }
11677         if (this.footer) {
11678             this.assetHeight += this.footer.getHeight();
11679         }
11680         */
11681             
11682         if(!this.tpl){
11683             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11684         }
11685
11686         this.view = new Roo.View(this.list, this.tpl, {
11687             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11688         });
11689         //this.view.wrapEl.setDisplayed(false);
11690         this.view.on('click', this.onViewClick, this);
11691         
11692         
11693         
11694         this.store.on('beforeload', this.onBeforeLoad, this);
11695         this.store.on('load', this.onLoad, this);
11696         this.store.on('loadexception', this.onLoadException, this);
11697         /*
11698         if(this.resizable){
11699             this.resizer = new Roo.Resizable(this.list,  {
11700                pinned:true, handles:'se'
11701             });
11702             this.resizer.on('resize', function(r, w, h){
11703                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11704                 this.listWidth = w;
11705                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11706                 this.restrictHeight();
11707             }, this);
11708             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11709         }
11710         */
11711         if(!this.editable){
11712             this.editable = true;
11713             this.setEditable(false);
11714         }
11715         
11716         /*
11717         
11718         if (typeof(this.events.add.listeners) != 'undefined') {
11719             
11720             this.addicon = this.wrap.createChild(
11721                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11722        
11723             this.addicon.on('click', function(e) {
11724                 this.fireEvent('add', this);
11725             }, this);
11726         }
11727         if (typeof(this.events.edit.listeners) != 'undefined') {
11728             
11729             this.editicon = this.wrap.createChild(
11730                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11731             if (this.addicon) {
11732                 this.editicon.setStyle('margin-left', '40px');
11733             }
11734             this.editicon.on('click', function(e) {
11735                 
11736                 // we fire even  if inothing is selected..
11737                 this.fireEvent('edit', this, this.lastData );
11738                 
11739             }, this);
11740         }
11741         */
11742         
11743         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11744             "up" : function(e){
11745                 this.inKeyMode = true;
11746                 this.selectPrev();
11747             },
11748
11749             "down" : function(e){
11750                 if(!this.isExpanded()){
11751                     this.onTriggerClick();
11752                 }else{
11753                     this.inKeyMode = true;
11754                     this.selectNext();
11755                 }
11756             },
11757
11758             "enter" : function(e){
11759 //                this.onViewClick();
11760                 //return true;
11761                 this.collapse();
11762                 
11763                 if(this.fireEvent("specialkey", this, e)){
11764                     this.onViewClick(false);
11765                 }
11766                 
11767                 return true;
11768             },
11769
11770             "esc" : function(e){
11771                 this.collapse();
11772             },
11773
11774             "tab" : function(e){
11775                 this.collapse();
11776                 
11777                 if(this.fireEvent("specialkey", this, e)){
11778                     this.onViewClick(false);
11779                 }
11780                 
11781                 return true;
11782             },
11783
11784             scope : this,
11785
11786             doRelay : function(foo, bar, hname){
11787                 if(hname == 'down' || this.scope.isExpanded()){
11788                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11789                 }
11790                 return true;
11791             },
11792
11793             forceKeyDown: true
11794         });
11795         
11796         
11797         this.queryDelay = Math.max(this.queryDelay || 10,
11798                 this.mode == 'local' ? 10 : 250);
11799         
11800         
11801         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11802         
11803         if(this.typeAhead){
11804             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11805         }
11806         if(this.editable !== false){
11807             this.inputEl().on("keyup", this.onKeyUp, this);
11808         }
11809         if(this.forceSelection){
11810             this.inputEl().on('blur', this.doForce, this);
11811         }
11812         
11813         if(this.multiple){
11814             this.choices = this.el.select('ul.select2-choices', true).first();
11815             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11816         }
11817     },
11818     
11819     initTickableEvents: function()
11820     {   
11821         this.createList();
11822         
11823         if(this.hiddenName){
11824             
11825             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11826             
11827             this.hiddenField.dom.value =
11828                 this.hiddenValue !== undefined ? this.hiddenValue :
11829                 this.value !== undefined ? this.value : '';
11830
11831             // prevent input submission
11832             this.el.dom.removeAttribute('name');
11833             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11834              
11835              
11836         }
11837         
11838 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11839         
11840         this.choices = this.el.select('ul.select2-choices', true).first();
11841         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11842         if(this.triggerList){
11843             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11844         }
11845          
11846         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11847         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11848         
11849         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11850         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11851         
11852         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11853         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11854         
11855         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11856         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11857         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11858         
11859         this.okBtn.hide();
11860         this.cancelBtn.hide();
11861         
11862         var _this = this;
11863         
11864         (function(){
11865             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11866             _this.list.setWidth(lw);
11867         }).defer(100);
11868         
11869         this.list.on('mouseover', this.onViewOver, this);
11870         this.list.on('mousemove', this.onViewMove, this);
11871         
11872         this.list.on('scroll', this.onViewScroll, this);
11873         
11874         if(!this.tpl){
11875             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>';
11876         }
11877
11878         this.view = new Roo.View(this.list, this.tpl, {
11879             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11880         });
11881         
11882         //this.view.wrapEl.setDisplayed(false);
11883         this.view.on('click', this.onViewClick, this);
11884         
11885         
11886         
11887         this.store.on('beforeload', this.onBeforeLoad, this);
11888         this.store.on('load', this.onLoad, this);
11889         this.store.on('loadexception', this.onLoadException, this);
11890         
11891         if(this.editable){
11892             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11893                 "up" : function(e){
11894                     this.inKeyMode = true;
11895                     this.selectPrev();
11896                 },
11897
11898                 "down" : function(e){
11899                     this.inKeyMode = true;
11900                     this.selectNext();
11901                 },
11902
11903                 "enter" : function(e){
11904                     if(this.fireEvent("specialkey", this, e)){
11905                         this.onViewClick(false);
11906                     }
11907                     
11908                     return true;
11909                 },
11910
11911                 "esc" : function(e){
11912                     this.onTickableFooterButtonClick(e, false, false);
11913                 },
11914
11915                 "tab" : function(e){
11916                     this.fireEvent("specialkey", this, e);
11917                     
11918                     this.onTickableFooterButtonClick(e, false, false);
11919                     
11920                     return true;
11921                 },
11922
11923                 scope : this,
11924
11925                 doRelay : function(e, fn, key){
11926                     if(this.scope.isExpanded()){
11927                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11928                     }
11929                     return true;
11930                 },
11931
11932                 forceKeyDown: true
11933             });
11934         }
11935         
11936         this.queryDelay = Math.max(this.queryDelay || 10,
11937                 this.mode == 'local' ? 10 : 250);
11938         
11939         
11940         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11941         
11942         if(this.typeAhead){
11943             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11944         }
11945         
11946         if(this.editable !== false){
11947             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11948         }
11949         
11950     },
11951
11952     onDestroy : function(){
11953         if(this.view){
11954             this.view.setStore(null);
11955             this.view.el.removeAllListeners();
11956             this.view.el.remove();
11957             this.view.purgeListeners();
11958         }
11959         if(this.list){
11960             this.list.dom.innerHTML  = '';
11961         }
11962         
11963         if(this.store){
11964             this.store.un('beforeload', this.onBeforeLoad, this);
11965             this.store.un('load', this.onLoad, this);
11966             this.store.un('loadexception', this.onLoadException, this);
11967         }
11968         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11969     },
11970
11971     // private
11972     fireKey : function(e){
11973         if(e.isNavKeyPress() && !this.list.isVisible()){
11974             this.fireEvent("specialkey", this, e);
11975         }
11976     },
11977
11978     // private
11979     onResize: function(w, h){
11980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11981 //        
11982 //        if(typeof w != 'number'){
11983 //            // we do not handle it!?!?
11984 //            return;
11985 //        }
11986 //        var tw = this.trigger.getWidth();
11987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11989 //        var x = w - tw;
11990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11991 //            
11992 //        //this.trigger.setStyle('left', x+'px');
11993 //        
11994 //        if(this.list && this.listWidth === undefined){
11995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11996 //            this.list.setWidth(lw);
11997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11998 //        }
11999         
12000     
12001         
12002     },
12003
12004     /**
12005      * Allow or prevent the user from directly editing the field text.  If false is passed,
12006      * the user will only be able to select from the items defined in the dropdown list.  This method
12007      * is the runtime equivalent of setting the 'editable' config option at config time.
12008      * @param {Boolean} value True to allow the user to directly edit the field text
12009      */
12010     setEditable : function(value){
12011         if(value == this.editable){
12012             return;
12013         }
12014         this.editable = value;
12015         if(!value){
12016             this.inputEl().dom.setAttribute('readOnly', true);
12017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12018             this.inputEl().addClass('x-combo-noedit');
12019         }else{
12020             this.inputEl().dom.setAttribute('readOnly', false);
12021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12022             this.inputEl().removeClass('x-combo-noedit');
12023         }
12024     },
12025
12026     // private
12027     
12028     onBeforeLoad : function(combo,opts){
12029         if(!this.hasFocus){
12030             return;
12031         }
12032          if (!opts.add) {
12033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12034          }
12035         this.restrictHeight();
12036         this.selectedIndex = -1;
12037     },
12038
12039     // private
12040     onLoad : function(){
12041         
12042         this.hasQuery = false;
12043         
12044         if(!this.hasFocus){
12045             return;
12046         }
12047         
12048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12049             this.loading.hide();
12050         }
12051              
12052         if(this.store.getCount() > 0){
12053             this.expand();
12054             this.restrictHeight();
12055             if(this.lastQuery == this.allQuery){
12056                 if(this.editable && !this.tickable){
12057                     this.inputEl().dom.select();
12058                 }
12059                 
12060                 if(
12061                     !this.selectByValue(this.value, true) &&
12062                     this.autoFocus && 
12063                     (
12064                         !this.store.lastOptions ||
12065                         typeof(this.store.lastOptions.add) == 'undefined' || 
12066                         this.store.lastOptions.add != true
12067                     )
12068                 ){
12069                     this.select(0, true);
12070                 }
12071             }else{
12072                 if(this.autoFocus){
12073                     this.selectNext();
12074                 }
12075                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12076                     this.taTask.delay(this.typeAheadDelay);
12077                 }
12078             }
12079         }else{
12080             this.onEmptyResults();
12081         }
12082         
12083         //this.el.focus();
12084     },
12085     // private
12086     onLoadException : function()
12087     {
12088         this.hasQuery = false;
12089         
12090         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12091             this.loading.hide();
12092         }
12093         
12094         if(this.tickable && this.editable){
12095             return;
12096         }
12097         
12098         this.collapse();
12099         
12100         Roo.log(this.store.reader.jsonData);
12101         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12102             // fixme
12103             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12104         }
12105         
12106         
12107     },
12108     // private
12109     onTypeAhead : function(){
12110         if(this.store.getCount() > 0){
12111             var r = this.store.getAt(0);
12112             var newValue = r.data[this.displayField];
12113             var len = newValue.length;
12114             var selStart = this.getRawValue().length;
12115             
12116             if(selStart != len){
12117                 this.setRawValue(newValue);
12118                 this.selectText(selStart, newValue.length);
12119             }
12120         }
12121     },
12122
12123     // private
12124     onSelect : function(record, index){
12125         
12126         if(this.fireEvent('beforeselect', this, record, index) !== false){
12127         
12128             this.setFromData(index > -1 ? record.data : false);
12129             
12130             this.collapse();
12131             this.fireEvent('select', this, record, index);
12132         }
12133     },
12134
12135     /**
12136      * Returns the currently selected field value or empty string if no value is set.
12137      * @return {String} value The selected value
12138      */
12139     getValue : function(){
12140         
12141         if(this.multiple){
12142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12143         }
12144         
12145         if(this.valueField){
12146             return typeof this.value != 'undefined' ? this.value : '';
12147         }else{
12148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12149         }
12150     },
12151
12152     /**
12153      * Clears any text/value currently set in the field
12154      */
12155     clearValue : function(){
12156         if(this.hiddenField){
12157             this.hiddenField.dom.value = '';
12158         }
12159         this.value = '';
12160         this.setRawValue('');
12161         this.lastSelectionText = '';
12162         this.lastData = false;
12163         
12164         var close = this.closeTriggerEl();
12165         
12166         if(close){
12167             close.hide();
12168         }
12169         
12170     },
12171
12172     /**
12173      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12174      * will be displayed in the field.  If the value does not match the data value of an existing item,
12175      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12176      * Otherwise the field will be blank (although the value will still be set).
12177      * @param {String} value The value to match
12178      */
12179     setValue : function(v){
12180         if(this.multiple){
12181             this.syncValue();
12182             return;
12183         }
12184         
12185         var text = v;
12186         if(this.valueField){
12187             var r = this.findRecord(this.valueField, v);
12188             if(r){
12189                 text = r.data[this.displayField];
12190             }else if(this.valueNotFoundText !== undefined){
12191                 text = this.valueNotFoundText;
12192             }
12193         }
12194         this.lastSelectionText = text;
12195         if(this.hiddenField){
12196             this.hiddenField.dom.value = v;
12197         }
12198         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12199         this.value = v;
12200         
12201         var close = this.closeTriggerEl();
12202         
12203         if(close){
12204             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12205         }
12206     },
12207     /**
12208      * @property {Object} the last set data for the element
12209      */
12210     
12211     lastData : false,
12212     /**
12213      * Sets the value of the field based on a object which is related to the record format for the store.
12214      * @param {Object} value the value to set as. or false on reset?
12215      */
12216     setFromData : function(o){
12217         
12218         if(this.multiple){
12219             this.addItem(o);
12220             return;
12221         }
12222             
12223         var dv = ''; // display value
12224         var vv = ''; // value value..
12225         this.lastData = o;
12226         if (this.displayField) {
12227             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12228         } else {
12229             // this is an error condition!!!
12230             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12231         }
12232         
12233         if(this.valueField){
12234             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12235         }
12236         
12237         var close = this.closeTriggerEl();
12238         
12239         if(close){
12240             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12241         }
12242         
12243         if(this.hiddenField){
12244             this.hiddenField.dom.value = vv;
12245             
12246             this.lastSelectionText = dv;
12247             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12248             this.value = vv;
12249             return;
12250         }
12251         // no hidden field.. - we store the value in 'value', but still display
12252         // display field!!!!
12253         this.lastSelectionText = dv;
12254         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12255         this.value = vv;
12256         
12257         
12258         
12259     },
12260     // private
12261     reset : function(){
12262         // overridden so that last data is reset..
12263         
12264         if(this.multiple){
12265             this.clearItem();
12266             return;
12267         }
12268         
12269         this.setValue(this.originalValue);
12270         this.clearInvalid();
12271         this.lastData = false;
12272         if (this.view) {
12273             this.view.clearSelections();
12274         }
12275     },
12276     // private
12277     findRecord : function(prop, value){
12278         var record;
12279         if(this.store.getCount() > 0){
12280             this.store.each(function(r){
12281                 if(r.data[prop] == value){
12282                     record = r;
12283                     return false;
12284                 }
12285                 return true;
12286             });
12287         }
12288         return record;
12289     },
12290     
12291     getName: function()
12292     {
12293         // returns hidden if it's set..
12294         if (!this.rendered) {return ''};
12295         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12296         
12297     },
12298     // private
12299     onViewMove : function(e, t){
12300         this.inKeyMode = false;
12301     },
12302
12303     // private
12304     onViewOver : function(e, t){
12305         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12306             return;
12307         }
12308         var item = this.view.findItemFromChild(t);
12309         
12310         if(item){
12311             var index = this.view.indexOf(item);
12312             this.select(index, false);
12313         }
12314     },
12315
12316     // private
12317     onViewClick : function(view, doFocus, el, e)
12318     {
12319         var index = this.view.getSelectedIndexes()[0];
12320         
12321         var r = this.store.getAt(index);
12322         
12323         if(this.tickable){
12324             
12325             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12326                 return;
12327             }
12328             
12329             var rm = false;
12330             var _this = this;
12331             
12332             Roo.each(this.tickItems, function(v,k){
12333                 
12334                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12335                     _this.tickItems.splice(k, 1);
12336                     
12337                     if(typeof(e) == 'undefined' && view == false){
12338                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12339                     }
12340                     
12341                     rm = true;
12342                     return;
12343                 }
12344             });
12345             
12346             if(rm){
12347                 return;
12348             }
12349             
12350             this.tickItems.push(r.data);
12351             
12352             if(typeof(e) == 'undefined' && view == false){
12353                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12354             }
12355                     
12356             return;
12357         }
12358         
12359         if(r){
12360             this.onSelect(r, index);
12361         }
12362         if(doFocus !== false && !this.blockFocus){
12363             this.inputEl().focus();
12364         }
12365     },
12366
12367     // private
12368     restrictHeight : function(){
12369         //this.innerList.dom.style.height = '';
12370         //var inner = this.innerList.dom;
12371         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12372         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12373         //this.list.beginUpdate();
12374         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12375         this.list.alignTo(this.inputEl(), this.listAlign);
12376         this.list.alignTo(this.inputEl(), this.listAlign);
12377         //this.list.endUpdate();
12378     },
12379
12380     // private
12381     onEmptyResults : function(){
12382         
12383         if(this.tickable && this.editable){
12384             this.restrictHeight();
12385             return;
12386         }
12387         
12388         this.collapse();
12389     },
12390
12391     /**
12392      * Returns true if the dropdown list is expanded, else false.
12393      */
12394     isExpanded : function(){
12395         return this.list.isVisible();
12396     },
12397
12398     /**
12399      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12400      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12401      * @param {String} value The data value of the item to select
12402      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12403      * selected item if it is not currently in view (defaults to true)
12404      * @return {Boolean} True if the value matched an item in the list, else false
12405      */
12406     selectByValue : function(v, scrollIntoView){
12407         if(v !== undefined && v !== null){
12408             var r = this.findRecord(this.valueField || this.displayField, v);
12409             if(r){
12410                 this.select(this.store.indexOf(r), scrollIntoView);
12411                 return true;
12412             }
12413         }
12414         return false;
12415     },
12416
12417     /**
12418      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12419      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12420      * @param {Number} index The zero-based index of the list item to select
12421      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12422      * selected item if it is not currently in view (defaults to true)
12423      */
12424     select : function(index, scrollIntoView){
12425         this.selectedIndex = index;
12426         this.view.select(index);
12427         if(scrollIntoView !== false){
12428             var el = this.view.getNode(index);
12429             /*
12430              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12431              */
12432             if(el){
12433                 this.list.scrollChildIntoView(el, false);
12434             }
12435         }
12436     },
12437
12438     // private
12439     selectNext : function(){
12440         var ct = this.store.getCount();
12441         if(ct > 0){
12442             if(this.selectedIndex == -1){
12443                 this.select(0);
12444             }else if(this.selectedIndex < ct-1){
12445                 this.select(this.selectedIndex+1);
12446             }
12447         }
12448     },
12449
12450     // private
12451     selectPrev : function(){
12452         var ct = this.store.getCount();
12453         if(ct > 0){
12454             if(this.selectedIndex == -1){
12455                 this.select(0);
12456             }else if(this.selectedIndex != 0){
12457                 this.select(this.selectedIndex-1);
12458             }
12459         }
12460     },
12461
12462     // private
12463     onKeyUp : function(e){
12464         if(this.editable !== false && !e.isSpecialKey()){
12465             this.lastKey = e.getKey();
12466             this.dqTask.delay(this.queryDelay);
12467         }
12468     },
12469
12470     // private
12471     validateBlur : function(){
12472         return !this.list || !this.list.isVisible();   
12473     },
12474
12475     // private
12476     initQuery : function(){
12477         
12478         var v = this.getRawValue();
12479         
12480         if(this.tickable && this.editable){
12481             v = this.tickableInputEl().getValue();
12482         }
12483         
12484         this.doQuery(v);
12485     },
12486
12487     // private
12488     doForce : function(){
12489         if(this.inputEl().dom.value.length > 0){
12490             this.inputEl().dom.value =
12491                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12492              
12493         }
12494     },
12495
12496     /**
12497      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12498      * query allowing the query action to be canceled if needed.
12499      * @param {String} query The SQL query to execute
12500      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12501      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12502      * saved in the current store (defaults to false)
12503      */
12504     doQuery : function(q, forceAll){
12505         
12506         if(q === undefined || q === null){
12507             q = '';
12508         }
12509         var qe = {
12510             query: q,
12511             forceAll: forceAll,
12512             combo: this,
12513             cancel:false
12514         };
12515         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12516             return false;
12517         }
12518         q = qe.query;
12519         
12520         forceAll = qe.forceAll;
12521         if(forceAll === true || (q.length >= this.minChars)){
12522             
12523             this.hasQuery = true;
12524             
12525             if(this.lastQuery != q || this.alwaysQuery){
12526                 this.lastQuery = q;
12527                 if(this.mode == 'local'){
12528                     this.selectedIndex = -1;
12529                     if(forceAll){
12530                         this.store.clearFilter();
12531                     }else{
12532                         
12533                         if(this.specialFilter){
12534                             this.fireEvent('specialfilter', this);
12535                             this.onLoad();
12536                             return;
12537                         }
12538                         
12539                         this.store.filter(this.displayField, q);
12540                     }
12541                     
12542                     this.store.fireEvent("datachanged", this.store);
12543                     
12544                     this.onLoad();
12545                     
12546                     
12547                 }else{
12548                     
12549                     this.store.baseParams[this.queryParam] = q;
12550                     
12551                     var options = {params : this.getParams(q)};
12552                     
12553                     if(this.loadNext){
12554                         options.add = true;
12555                         options.params.start = this.page * this.pageSize;
12556                     }
12557                     
12558                     this.store.load(options);
12559                     
12560                     /*
12561                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12562                      *  we should expand the list on onLoad
12563                      *  so command out it
12564                      */
12565 //                    this.expand();
12566                 }
12567             }else{
12568                 this.selectedIndex = -1;
12569                 this.onLoad();   
12570             }
12571         }
12572         
12573         this.loadNext = false;
12574     },
12575     
12576     // private
12577     getParams : function(q){
12578         var p = {};
12579         //p[this.queryParam] = q;
12580         
12581         if(this.pageSize){
12582             p.start = 0;
12583             p.limit = this.pageSize;
12584         }
12585         return p;
12586     },
12587
12588     /**
12589      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12590      */
12591     collapse : function(){
12592         if(!this.isExpanded()){
12593             return;
12594         }
12595         
12596         this.list.hide();
12597         
12598         if(this.tickable){
12599             this.hasFocus = false;
12600             this.okBtn.hide();
12601             this.cancelBtn.hide();
12602             this.trigger.show();
12603             
12604             if(this.editable){
12605                 this.tickableInputEl().dom.value = '';
12606                 this.tickableInputEl().blur();
12607             }
12608             
12609         }
12610         
12611         Roo.get(document).un('mousedown', this.collapseIf, this);
12612         Roo.get(document).un('mousewheel', this.collapseIf, this);
12613         if (!this.editable) {
12614             Roo.get(document).un('keydown', this.listKeyPress, this);
12615         }
12616         this.fireEvent('collapse', this);
12617     },
12618
12619     // private
12620     collapseIf : function(e){
12621         var in_combo  = e.within(this.el);
12622         var in_list =  e.within(this.list);
12623         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12624         
12625         if (in_combo || in_list || is_list) {
12626             //e.stopPropagation();
12627             return;
12628         }
12629         
12630         if(this.tickable){
12631             this.onTickableFooterButtonClick(e, false, false);
12632         }
12633
12634         this.collapse();
12635         
12636     },
12637
12638     /**
12639      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12640      */
12641     expand : function(){
12642        
12643         if(this.isExpanded() || !this.hasFocus){
12644             return;
12645         }
12646         
12647         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12648         this.list.setWidth(lw);
12649         
12650         
12651          Roo.log('expand');
12652         
12653         this.list.show();
12654         
12655         this.restrictHeight();
12656         
12657         if(this.tickable){
12658             
12659             this.tickItems = Roo.apply([], this.item);
12660             
12661             this.okBtn.show();
12662             this.cancelBtn.show();
12663             this.trigger.hide();
12664             
12665             if(this.editable){
12666                 this.tickableInputEl().focus();
12667             }
12668             
12669         }
12670         
12671         Roo.get(document).on('mousedown', this.collapseIf, this);
12672         Roo.get(document).on('mousewheel', this.collapseIf, this);
12673         if (!this.editable) {
12674             Roo.get(document).on('keydown', this.listKeyPress, this);
12675         }
12676         
12677         this.fireEvent('expand', this);
12678     },
12679
12680     // private
12681     // Implements the default empty TriggerField.onTriggerClick function
12682     onTriggerClick : function(e)
12683     {
12684         Roo.log('trigger click');
12685         
12686         if(this.disabled || !this.triggerList){
12687             return;
12688         }
12689         
12690         this.page = 0;
12691         this.loadNext = false;
12692         
12693         if(this.isExpanded()){
12694             this.collapse();
12695             if (!this.blockFocus) {
12696                 this.inputEl().focus();
12697             }
12698             
12699         }else {
12700             this.hasFocus = true;
12701             if(this.triggerAction == 'all') {
12702                 this.doQuery(this.allQuery, true);
12703             } else {
12704                 this.doQuery(this.getRawValue());
12705             }
12706             if (!this.blockFocus) {
12707                 this.inputEl().focus();
12708             }
12709         }
12710     },
12711     
12712     onTickableTriggerClick : function(e)
12713     {
12714         if(this.disabled){
12715             return;
12716         }
12717         
12718         this.page = 0;
12719         this.loadNext = false;
12720         this.hasFocus = true;
12721         
12722         if(this.triggerAction == 'all') {
12723             this.doQuery(this.allQuery, true);
12724         } else {
12725             this.doQuery(this.getRawValue());
12726         }
12727     },
12728     
12729     onSearchFieldClick : function(e)
12730     {
12731         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12732             this.onTickableFooterButtonClick(e, false, false);
12733             return;
12734         }
12735         
12736         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12737             return;
12738         }
12739         
12740         this.page = 0;
12741         this.loadNext = false;
12742         this.hasFocus = true;
12743         
12744         if(this.triggerAction == 'all') {
12745             this.doQuery(this.allQuery, true);
12746         } else {
12747             this.doQuery(this.getRawValue());
12748         }
12749     },
12750     
12751     listKeyPress : function(e)
12752     {
12753         //Roo.log('listkeypress');
12754         // scroll to first matching element based on key pres..
12755         if (e.isSpecialKey()) {
12756             return false;
12757         }
12758         var k = String.fromCharCode(e.getKey()).toUpperCase();
12759         //Roo.log(k);
12760         var match  = false;
12761         var csel = this.view.getSelectedNodes();
12762         var cselitem = false;
12763         if (csel.length) {
12764             var ix = this.view.indexOf(csel[0]);
12765             cselitem  = this.store.getAt(ix);
12766             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12767                 cselitem = false;
12768             }
12769             
12770         }
12771         
12772         this.store.each(function(v) { 
12773             if (cselitem) {
12774                 // start at existing selection.
12775                 if (cselitem.id == v.id) {
12776                     cselitem = false;
12777                 }
12778                 return true;
12779             }
12780                 
12781             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12782                 match = this.store.indexOf(v);
12783                 return false;
12784             }
12785             return true;
12786         }, this);
12787         
12788         if (match === false) {
12789             return true; // no more action?
12790         }
12791         // scroll to?
12792         this.view.select(match);
12793         var sn = Roo.get(this.view.getSelectedNodes()[0])
12794         sn.scrollIntoView(sn.dom.parentNode, false);
12795     },
12796     
12797     onViewScroll : function(e, t){
12798         
12799         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){
12800             return;
12801         }
12802         
12803         this.hasQuery = true;
12804         
12805         this.loading = this.list.select('.loading', true).first();
12806         
12807         if(this.loading === null){
12808             this.list.createChild({
12809                 tag: 'div',
12810                 cls: 'loading select2-more-results select2-active',
12811                 html: 'Loading more results...'
12812             })
12813             
12814             this.loading = this.list.select('.loading', true).first();
12815             
12816             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12817             
12818             this.loading.hide();
12819         }
12820         
12821         this.loading.show();
12822         
12823         var _combo = this;
12824         
12825         this.page++;
12826         this.loadNext = true;
12827         
12828         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12829         
12830         return;
12831     },
12832     
12833     addItem : function(o)
12834     {   
12835         var dv = ''; // display value
12836         
12837         if (this.displayField) {
12838             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12839         } else {
12840             // this is an error condition!!!
12841             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12842         }
12843         
12844         if(!dv.length){
12845             return;
12846         }
12847         
12848         var choice = this.choices.createChild({
12849             tag: 'li',
12850             cls: 'select2-search-choice',
12851             cn: [
12852                 {
12853                     tag: 'div',
12854                     html: dv
12855                 },
12856                 {
12857                     tag: 'a',
12858                     href: '#',
12859                     cls: 'select2-search-choice-close',
12860                     tabindex: '-1'
12861                 }
12862             ]
12863             
12864         }, this.searchField);
12865         
12866         var close = choice.select('a.select2-search-choice-close', true).first()
12867         
12868         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12869         
12870         this.item.push(o);
12871         
12872         this.lastData = o;
12873         
12874         this.syncValue();
12875         
12876         this.inputEl().dom.value = '';
12877         
12878         this.validate();
12879     },
12880     
12881     onRemoveItem : function(e, _self, o)
12882     {
12883         e.preventDefault();
12884         
12885         this.lastItem = Roo.apply([], this.item);
12886         
12887         var index = this.item.indexOf(o.data) * 1;
12888         
12889         if( index < 0){
12890             Roo.log('not this item?!');
12891             return;
12892         }
12893         
12894         this.item.splice(index, 1);
12895         o.item.remove();
12896         
12897         this.syncValue();
12898         
12899         this.fireEvent('remove', this, e);
12900         
12901         this.validate();
12902         
12903     },
12904     
12905     syncValue : function()
12906     {
12907         if(!this.item.length){
12908             this.clearValue();
12909             return;
12910         }
12911             
12912         var value = [];
12913         var _this = this;
12914         Roo.each(this.item, function(i){
12915             if(_this.valueField){
12916                 value.push(i[_this.valueField]);
12917                 return;
12918             }
12919
12920             value.push(i);
12921         });
12922
12923         this.value = value.join(',');
12924
12925         if(this.hiddenField){
12926             this.hiddenField.dom.value = this.value;
12927         }
12928         
12929         this.store.fireEvent("datachanged", this.store);
12930     },
12931     
12932     clearItem : function()
12933     {
12934         if(!this.multiple){
12935             return;
12936         }
12937         
12938         this.item = [];
12939         
12940         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12941            c.remove();
12942         });
12943         
12944         this.syncValue();
12945         
12946         this.validate();
12947         
12948         if(this.tickable && !Roo.isTouch){
12949             this.view.refresh();
12950         }
12951     },
12952     
12953     inputEl: function ()
12954     {
12955         if(Roo.isTouch && this.mobileTouchView){
12956             return this.el.select('input.form-control',true).first();
12957         }
12958         
12959         if(this.tickable){
12960             return this.searchField;
12961         }
12962         
12963         return this.el.select('input.form-control',true).first();
12964     },
12965     
12966     
12967     onTickableFooterButtonClick : function(e, btn, el)
12968     {
12969         e.preventDefault();
12970         
12971         this.lastItem = Roo.apply([], this.item);
12972         
12973         if(btn && btn.name == 'cancel'){
12974             this.tickItems = Roo.apply([], this.item);
12975             this.collapse();
12976             return;
12977         }
12978         
12979         this.clearItem();
12980         
12981         var _this = this;
12982         
12983         Roo.each(this.tickItems, function(o){
12984             _this.addItem(o);
12985         });
12986         
12987         this.collapse();
12988         
12989     },
12990     
12991     validate : function()
12992     {
12993         var v = this.getRawValue();
12994         
12995         if(this.multiple){
12996             v = this.getValue();
12997         }
12998         
12999         if(this.disabled || this.allowBlank || v.length){
13000             this.markValid();
13001             return true;
13002         }
13003         
13004         this.markInvalid();
13005         return false;
13006     },
13007     
13008     tickableInputEl : function()
13009     {
13010         if(!this.tickable || !this.editable){
13011             return this.inputEl();
13012         }
13013         
13014         return this.inputEl().select('.select2-search-field-input', true).first();
13015     },
13016     
13017     
13018     getAutoCreateTouchView : function()
13019     {
13020         var id = Roo.id();
13021         
13022         var cfg = {
13023             cls: 'form-group' //input-group
13024         };
13025         
13026         var input =  {
13027             tag: 'input',
13028             id : id,
13029             type : this.inputType,
13030             cls : 'form-control x-combo-noedit',
13031             autocomplete: 'new-password',
13032             placeholder : this.placeholder || '',
13033             readonly : true
13034         };
13035         
13036         if (this.name) {
13037             input.name = this.name;
13038         }
13039         
13040         if (this.size) {
13041             input.cls += ' input-' + this.size;
13042         }
13043         
13044         if (this.disabled) {
13045             input.disabled = true;
13046         }
13047         
13048         var inputblock = {
13049             cls : '',
13050             cn : [
13051                 input
13052             ]
13053         };
13054         
13055         if(this.before){
13056             inputblock.cls += ' input-group';
13057             
13058             inputblock.cn.unshift({
13059                 tag :'span',
13060                 cls : 'input-group-addon',
13061                 html : this.before
13062             });
13063         }
13064         
13065         if(this.removable && !this.multiple){
13066             inputblock.cls += ' roo-removable';
13067             
13068             inputblock.cn.push({
13069                 tag: 'button',
13070                 html : 'x',
13071                 cls : 'roo-combo-removable-btn close'
13072             });
13073         }
13074
13075         if(this.hasFeedback && !this.allowBlank){
13076             
13077             inputblock.cls += ' has-feedback';
13078             
13079             inputblock.cn.push({
13080                 tag: 'span',
13081                 cls: 'glyphicon form-control-feedback'
13082             });
13083             
13084         }
13085         
13086         if (this.after) {
13087             
13088             inputblock.cls += (this.before) ? '' : ' input-group';
13089             
13090             inputblock.cn.push({
13091                 tag :'span',
13092                 cls : 'input-group-addon',
13093                 html : this.after
13094             });
13095         }
13096
13097         var box = {
13098             tag: 'div',
13099             cn: [
13100                 {
13101                     tag: 'input',
13102                     type : 'hidden',
13103                     cls: 'form-hidden-field'
13104                 },
13105                 inputblock
13106             ]
13107             
13108         };
13109         
13110         if(this.multiple){
13111             box = {
13112                 tag: 'div',
13113                 cn: [
13114                     {
13115                         tag: 'input',
13116                         type : 'hidden',
13117                         cls: 'form-hidden-field'
13118                     },
13119                     {
13120                         tag: 'ul',
13121                         cls: 'select2-choices',
13122                         cn:[
13123                             {
13124                                 tag: 'li',
13125                                 cls: 'select2-search-field',
13126                                 cn: [
13127
13128                                     inputblock
13129                                 ]
13130                             }
13131                         ]
13132                     }
13133                 ]
13134             }
13135         };
13136         
13137         var combobox = {
13138             cls: 'select2-container input-group',
13139             cn: [
13140                 box
13141             ]
13142         };
13143         
13144         if(this.multiple){
13145             combobox.cls += ' select2-container-multi';
13146         }
13147         
13148         var align = this.labelAlign || this.parentLabelAlign();
13149         
13150         cfg.cn = combobox;
13151         
13152         if(this.fieldLabel.length){
13153             
13154             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13155             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13156             
13157             cfg.cn = [
13158                 {
13159                     tag: 'label',
13160                     cls : 'control-label ' + lw,
13161                     html : this.fieldLabel
13162
13163                 },
13164                 {
13165                     cls : cw, 
13166                     cn: [
13167                         combobox
13168                     ]
13169                 }
13170             ];
13171         }
13172         
13173         var settings = this;
13174         
13175         ['xs','sm','md','lg'].map(function(size){
13176             if (settings[size]) {
13177                 cfg.cls += ' col-' + size + '-' + settings[size];
13178             }
13179         });
13180         
13181         return cfg;
13182     },
13183     
13184     initTouchView : function()
13185     {
13186         this.renderTouchView();
13187         
13188         this.touchViewEl.on('scroll', function(){
13189             this.el.dom.scrollTop = 0;
13190         }, this);
13191         
13192         this.inputEl().on("click", this.showTouchView, this);
13193         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13194         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13195         
13196         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13197         
13198         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13199         this.store.on('load', this.onTouchViewLoad, this);
13200         this.store.on('loadexception', this.onTouchViewLoadException, this);
13201         
13202         if(this.hiddenName){
13203             
13204             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13205             
13206             this.hiddenField.dom.value =
13207                 this.hiddenValue !== undefined ? this.hiddenValue :
13208                 this.value !== undefined ? this.value : '';
13209         
13210             this.el.dom.removeAttribute('name');
13211             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13212         }
13213         
13214         if(this.multiple){
13215             this.choices = this.el.select('ul.select2-choices', true).first();
13216             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13217         }
13218         
13219         if(this.removable && !this.multiple){
13220             var close = this.closeTriggerEl();
13221             if(close){
13222                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13223                 close.on('click', this.removeBtnClick, this, close);
13224             }
13225         }
13226         
13227         return;
13228         
13229         
13230     },
13231     
13232     renderTouchView : function()
13233     {
13234         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13235         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13236         
13237         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13238         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13239         
13240         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13241         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13242         this.touchViewBodyEl.setStyle('overflow', 'auto');
13243         
13244         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13245         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13246         
13247         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13248         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13249         
13250     },
13251     
13252     showTouchView : function()
13253     {
13254         this.touchViewHeaderEl.hide();
13255
13256         if(this.fieldLabel.length){
13257             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13258             this.touchViewHeaderEl.show();
13259         }
13260
13261         this.touchViewEl.show();
13262
13263         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13264         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13265
13266         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13267
13268         if(this.fieldLabel.length){
13269             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13270         }
13271         
13272         this.touchViewBodyEl.setHeight(bodyHeight);
13273
13274         if(this.animate){
13275             var _this = this;
13276             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13277         }else{
13278             this.touchViewEl.addClass('in');
13279         }
13280
13281         this.doTouchViewQuery();
13282         
13283     },
13284     
13285     hideTouchView : function()
13286     {
13287         this.touchViewEl.removeClass('in');
13288
13289         if(this.animate){
13290             var _this = this;
13291             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13292         }else{
13293             this.touchViewEl.setStyle('display', 'none');
13294         }
13295         
13296     },
13297     
13298     setTouchViewValue : function()
13299     {
13300         if(this.multiple){
13301             this.clearItem();
13302         
13303             var _this = this;
13304
13305             Roo.each(this.tickItems, function(o){
13306                 this.addItem(o);
13307             }, this);
13308         }
13309         
13310         this.hideTouchView();
13311     },
13312     
13313     doTouchViewQuery : function()
13314     {
13315         var qe = {
13316             query: '',
13317             forceAll: true,
13318             combo: this,
13319             cancel:false
13320         };
13321         
13322         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13323             return false;
13324         }
13325         
13326         if(!this.alwaysQuery || this.mode == 'local'){
13327             this.onTouchViewLoad();
13328             return;
13329         }
13330         
13331         this.store.load();
13332     },
13333     
13334     onTouchViewBeforeLoad : function(combo,opts)
13335     {
13336         return;
13337     },
13338
13339     // private
13340     onTouchViewLoad : function()
13341     {
13342         if(this.store.getCount() < 1){
13343             this.onTouchViewEmptyResults();
13344             return;
13345         }
13346         
13347         this.clearTouchView();
13348         
13349         var rawValue = this.getRawValue();
13350         
13351         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13352         
13353         this.tickItems = [];
13354         
13355         this.store.data.each(function(d, rowIndex){
13356             var row = this.touchViewListGroup.createChild(template);
13357             
13358             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13359                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13360             }
13361             
13362             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13363                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13364             }
13365             
13366             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13367                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13368                 this.tickItems.push(d.data);
13369             }
13370             
13371             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13372             
13373         }, this);
13374         
13375         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13376         
13377         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13378
13379         if(this.fieldLabel.length){
13380             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13381         }
13382
13383         var listHeight = this.touchViewListGroup.getHeight();
13384         
13385         var _this = this;
13386         
13387         if(firstChecked && listHeight > bodyHeight){
13388             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13389         }
13390         
13391     },
13392     
13393     onTouchViewLoadException : function()
13394     {
13395         this.hideTouchView();
13396     },
13397     
13398     onTouchViewEmptyResults : function()
13399     {
13400         this.clearTouchView();
13401         
13402         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13403         
13404         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13405         
13406     },
13407     
13408     clearTouchView : function()
13409     {
13410         this.touchViewListGroup.dom.innerHTML = '';
13411     },
13412     
13413     onTouchViewClick : function(e, el, o)
13414     {
13415         e.preventDefault();
13416         
13417         var row = o.row;
13418         var rowIndex = o.rowIndex;
13419         
13420         var r = this.store.getAt(rowIndex);
13421         
13422         if(!this.multiple){
13423             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13424                 c.dom.removeAttribute('checked');
13425             }, this);
13426             
13427             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13428         
13429             this.setFromData(r.data);
13430             
13431             var close = this.closeTriggerEl();
13432         
13433             if(close){
13434                 close.show();
13435             }
13436
13437             this.hideTouchView();
13438             
13439             this.fireEvent('select', this, r, rowIndex);
13440             
13441             return;
13442         }
13443         
13444         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13445             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13446             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13447             return;
13448         }
13449         
13450         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13451         this.addItem(r.data);
13452         this.tickItems.push(r.data);
13453         
13454     }
13455     
13456
13457     /** 
13458     * @cfg {Boolean} grow 
13459     * @hide 
13460     */
13461     /** 
13462     * @cfg {Number} growMin 
13463     * @hide 
13464     */
13465     /** 
13466     * @cfg {Number} growMax 
13467     * @hide 
13468     */
13469     /**
13470      * @hide
13471      * @method autoSize
13472      */
13473 });
13474
13475 Roo.apply(Roo.bootstrap.ComboBox,  {
13476     
13477     header : {
13478         tag: 'div',
13479         cls: 'modal-header',
13480         cn: [
13481             {
13482                 tag: 'h4',
13483                 cls: 'modal-title'
13484             }
13485         ]
13486     },
13487     
13488     body : {
13489         tag: 'div',
13490         cls: 'modal-body',
13491         cn: [
13492             {
13493                 tag: 'ul',
13494                 cls: 'list-group'
13495             }
13496         ]
13497     },
13498     
13499     listItemRadio : {
13500         tag: 'li',
13501         cls: 'list-group-item',
13502         cn: [
13503             {
13504                 tag: 'span',
13505                 cls: 'roo-combobox-list-group-item-value'
13506             },
13507             {
13508                 tag: 'div',
13509                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13510                 cn: [
13511                     {
13512                         tag: 'input',
13513                         type: 'radio'
13514                     },
13515                     {
13516                         tag: 'label'
13517                     }
13518                 ]
13519             }
13520         ]
13521     },
13522     
13523     listItemCheckbox : {
13524         tag: 'li',
13525         cls: 'list-group-item',
13526         cn: [
13527             {
13528                 tag: 'span',
13529                 cls: 'roo-combobox-list-group-item-value'
13530             },
13531             {
13532                 tag: 'div',
13533                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13534                 cn: [
13535                     {
13536                         tag: 'input',
13537                         type: 'checkbox'
13538                     },
13539                     {
13540                         tag: 'label'
13541                     }
13542                 ]
13543             }
13544         ]
13545     },
13546     
13547     emptyResult : {
13548         tag: 'div',
13549         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13550     },
13551     
13552     footer : {
13553         tag: 'div',
13554         cls: 'modal-footer',
13555         cn: [
13556             {
13557                 tag: 'div',
13558                 cls: 'row',
13559                 cn: [
13560                     {
13561                         tag: 'div',
13562                         cls: 'col-xs-6 text-left',
13563                         cn: {
13564                             tag: 'button',
13565                             cls: 'btn btn-danger roo-touch-view-cancel',
13566                             html: 'Cancel'
13567                         }
13568                     },
13569                     {
13570                         tag: 'div',
13571                         cls: 'col-xs-6 text-right',
13572                         cn: {
13573                             tag: 'button',
13574                             cls: 'btn btn-success roo-touch-view-ok',
13575                             html: 'OK'
13576                         }
13577                     }
13578                 ]
13579             }
13580         ]
13581         
13582     }
13583 });
13584
13585 Roo.apply(Roo.bootstrap.ComboBox,  {
13586     
13587     touchViewTemplate : {
13588         tag: 'div',
13589         cls: 'modal fade roo-combobox-touch-view',
13590         cn: [
13591             {
13592                 tag: 'div',
13593                 cls: 'modal-dialog',
13594                 cn: [
13595                     {
13596                         tag: 'div',
13597                         cls: 'modal-content',
13598                         cn: [
13599                             Roo.bootstrap.ComboBox.header,
13600                             Roo.bootstrap.ComboBox.body,
13601                             Roo.bootstrap.ComboBox.footer
13602                         ]
13603                     }
13604                 ]
13605             }
13606         ]
13607     }
13608 });/*
13609  * Based on:
13610  * Ext JS Library 1.1.1
13611  * Copyright(c) 2006-2007, Ext JS, LLC.
13612  *
13613  * Originally Released Under LGPL - original licence link has changed is not relivant.
13614  *
13615  * Fork - LGPL
13616  * <script type="text/javascript">
13617  */
13618
13619 /**
13620  * @class Roo.View
13621  * @extends Roo.util.Observable
13622  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13623  * This class also supports single and multi selection modes. <br>
13624  * Create a data model bound view:
13625  <pre><code>
13626  var store = new Roo.data.Store(...);
13627
13628  var view = new Roo.View({
13629     el : "my-element",
13630     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13631  
13632     singleSelect: true,
13633     selectedClass: "ydataview-selected",
13634     store: store
13635  });
13636
13637  // listen for node click?
13638  view.on("click", function(vw, index, node, e){
13639  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13640  });
13641
13642  // load XML data
13643  dataModel.load("foobar.xml");
13644  </code></pre>
13645  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13646  * <br><br>
13647  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13648  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13649  * 
13650  * Note: old style constructor is still suported (container, template, config)
13651  * 
13652  * @constructor
13653  * Create a new View
13654  * @param {Object} config The config object
13655  * 
13656  */
13657 Roo.View = function(config, depreciated_tpl, depreciated_config){
13658     
13659     this.parent = false;
13660     
13661     if (typeof(depreciated_tpl) == 'undefined') {
13662         // new way.. - universal constructor.
13663         Roo.apply(this, config);
13664         this.el  = Roo.get(this.el);
13665     } else {
13666         // old format..
13667         this.el  = Roo.get(config);
13668         this.tpl = depreciated_tpl;
13669         Roo.apply(this, depreciated_config);
13670     }
13671     this.wrapEl  = this.el.wrap().wrap();
13672     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13673     
13674     
13675     if(typeof(this.tpl) == "string"){
13676         this.tpl = new Roo.Template(this.tpl);
13677     } else {
13678         // support xtype ctors..
13679         this.tpl = new Roo.factory(this.tpl, Roo);
13680     }
13681     
13682     
13683     this.tpl.compile();
13684     
13685     /** @private */
13686     this.addEvents({
13687         /**
13688          * @event beforeclick
13689          * Fires before a click is processed. Returns false to cancel the default action.
13690          * @param {Roo.View} this
13691          * @param {Number} index The index of the target node
13692          * @param {HTMLElement} node The target node
13693          * @param {Roo.EventObject} e The raw event object
13694          */
13695             "beforeclick" : true,
13696         /**
13697          * @event click
13698          * Fires when a template node is clicked.
13699          * @param {Roo.View} this
13700          * @param {Number} index The index of the target node
13701          * @param {HTMLElement} node The target node
13702          * @param {Roo.EventObject} e The raw event object
13703          */
13704             "click" : true,
13705         /**
13706          * @event dblclick
13707          * Fires when a template node is double clicked.
13708          * @param {Roo.View} this
13709          * @param {Number} index The index of the target node
13710          * @param {HTMLElement} node The target node
13711          * @param {Roo.EventObject} e The raw event object
13712          */
13713             "dblclick" : true,
13714         /**
13715          * @event contextmenu
13716          * Fires when a template node is right clicked.
13717          * @param {Roo.View} this
13718          * @param {Number} index The index of the target node
13719          * @param {HTMLElement} node The target node
13720          * @param {Roo.EventObject} e The raw event object
13721          */
13722             "contextmenu" : true,
13723         /**
13724          * @event selectionchange
13725          * Fires when the selected nodes change.
13726          * @param {Roo.View} this
13727          * @param {Array} selections Array of the selected nodes
13728          */
13729             "selectionchange" : true,
13730     
13731         /**
13732          * @event beforeselect
13733          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13734          * @param {Roo.View} this
13735          * @param {HTMLElement} node The node to be selected
13736          * @param {Array} selections Array of currently selected nodes
13737          */
13738             "beforeselect" : true,
13739         /**
13740          * @event preparedata
13741          * Fires on every row to render, to allow you to change the data.
13742          * @param {Roo.View} this
13743          * @param {Object} data to be rendered (change this)
13744          */
13745           "preparedata" : true
13746           
13747           
13748         });
13749
13750
13751
13752     this.el.on({
13753         "click": this.onClick,
13754         "dblclick": this.onDblClick,
13755         "contextmenu": this.onContextMenu,
13756         scope:this
13757     });
13758
13759     this.selections = [];
13760     this.nodes = [];
13761     this.cmp = new Roo.CompositeElementLite([]);
13762     if(this.store){
13763         this.store = Roo.factory(this.store, Roo.data);
13764         this.setStore(this.store, true);
13765     }
13766     
13767     if ( this.footer && this.footer.xtype) {
13768            
13769          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13770         
13771         this.footer.dataSource = this.store
13772         this.footer.container = fctr;
13773         this.footer = Roo.factory(this.footer, Roo);
13774         fctr.insertFirst(this.el);
13775         
13776         // this is a bit insane - as the paging toolbar seems to detach the el..
13777 //        dom.parentNode.parentNode.parentNode
13778          // they get detached?
13779     }
13780     
13781     
13782     Roo.View.superclass.constructor.call(this);
13783     
13784     
13785 };
13786
13787 Roo.extend(Roo.View, Roo.util.Observable, {
13788     
13789      /**
13790      * @cfg {Roo.data.Store} store Data store to load data from.
13791      */
13792     store : false,
13793     
13794     /**
13795      * @cfg {String|Roo.Element} el The container element.
13796      */
13797     el : '',
13798     
13799     /**
13800      * @cfg {String|Roo.Template} tpl The template used by this View 
13801      */
13802     tpl : false,
13803     /**
13804      * @cfg {String} dataName the named area of the template to use as the data area
13805      *                          Works with domtemplates roo-name="name"
13806      */
13807     dataName: false,
13808     /**
13809      * @cfg {String} selectedClass The css class to add to selected nodes
13810      */
13811     selectedClass : "x-view-selected",
13812      /**
13813      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13814      */
13815     emptyText : "",
13816     
13817     /**
13818      * @cfg {String} text to display on mask (default Loading)
13819      */
13820     mask : false,
13821     /**
13822      * @cfg {Boolean} multiSelect Allow multiple selection
13823      */
13824     multiSelect : false,
13825     /**
13826      * @cfg {Boolean} singleSelect Allow single selection
13827      */
13828     singleSelect:  false,
13829     
13830     /**
13831      * @cfg {Boolean} toggleSelect - selecting 
13832      */
13833     toggleSelect : false,
13834     
13835     /**
13836      * @cfg {Boolean} tickable - selecting 
13837      */
13838     tickable : false,
13839     
13840     /**
13841      * Returns the element this view is bound to.
13842      * @return {Roo.Element}
13843      */
13844     getEl : function(){
13845         return this.wrapEl;
13846     },
13847     
13848     
13849
13850     /**
13851      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13852      */
13853     refresh : function(){
13854         //Roo.log('refresh');
13855         var t = this.tpl;
13856         
13857         // if we are using something like 'domtemplate', then
13858         // the what gets used is:
13859         // t.applySubtemplate(NAME, data, wrapping data..)
13860         // the outer template then get' applied with
13861         //     the store 'extra data'
13862         // and the body get's added to the
13863         //      roo-name="data" node?
13864         //      <span class='roo-tpl-{name}'></span> ?????
13865         
13866         
13867         
13868         this.clearSelections();
13869         this.el.update("");
13870         var html = [];
13871         var records = this.store.getRange();
13872         if(records.length < 1) {
13873             
13874             // is this valid??  = should it render a template??
13875             
13876             this.el.update(this.emptyText);
13877             return;
13878         }
13879         var el = this.el;
13880         if (this.dataName) {
13881             this.el.update(t.apply(this.store.meta)); //????
13882             el = this.el.child('.roo-tpl-' + this.dataName);
13883         }
13884         
13885         for(var i = 0, len = records.length; i < len; i++){
13886             var data = this.prepareData(records[i].data, i, records[i]);
13887             this.fireEvent("preparedata", this, data, i, records[i]);
13888             
13889             var d = Roo.apply({}, data);
13890             
13891             if(this.tickable){
13892                 Roo.apply(d, {'roo-id' : Roo.id()});
13893                 
13894                 var _this = this;
13895             
13896                 Roo.each(this.parent.item, function(item){
13897                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13898                         return;
13899                     }
13900                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13901                 });
13902             }
13903             
13904             html[html.length] = Roo.util.Format.trim(
13905                 this.dataName ?
13906                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13907                     t.apply(d)
13908             );
13909         }
13910         
13911         
13912         
13913         el.update(html.join(""));
13914         this.nodes = el.dom.childNodes;
13915         this.updateIndexes(0);
13916     },
13917     
13918
13919     /**
13920      * Function to override to reformat the data that is sent to
13921      * the template for each node.
13922      * DEPRICATED - use the preparedata event handler.
13923      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13924      * a JSON object for an UpdateManager bound view).
13925      */
13926     prepareData : function(data, index, record)
13927     {
13928         this.fireEvent("preparedata", this, data, index, record);
13929         return data;
13930     },
13931
13932     onUpdate : function(ds, record){
13933         // Roo.log('on update');   
13934         this.clearSelections();
13935         var index = this.store.indexOf(record);
13936         var n = this.nodes[index];
13937         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13938         n.parentNode.removeChild(n);
13939         this.updateIndexes(index, index);
13940     },
13941
13942     
13943     
13944 // --------- FIXME     
13945     onAdd : function(ds, records, index)
13946     {
13947         //Roo.log(['on Add', ds, records, index] );        
13948         this.clearSelections();
13949         if(this.nodes.length == 0){
13950             this.refresh();
13951             return;
13952         }
13953         var n = this.nodes[index];
13954         for(var i = 0, len = records.length; i < len; i++){
13955             var d = this.prepareData(records[i].data, i, records[i]);
13956             if(n){
13957                 this.tpl.insertBefore(n, d);
13958             }else{
13959                 
13960                 this.tpl.append(this.el, d);
13961             }
13962         }
13963         this.updateIndexes(index);
13964     },
13965
13966     onRemove : function(ds, record, index){
13967        // Roo.log('onRemove');
13968         this.clearSelections();
13969         var el = this.dataName  ?
13970             this.el.child('.roo-tpl-' + this.dataName) :
13971             this.el; 
13972         
13973         el.dom.removeChild(this.nodes[index]);
13974         this.updateIndexes(index);
13975     },
13976
13977     /**
13978      * Refresh an individual node.
13979      * @param {Number} index
13980      */
13981     refreshNode : function(index){
13982         this.onUpdate(this.store, this.store.getAt(index));
13983     },
13984
13985     updateIndexes : function(startIndex, endIndex){
13986         var ns = this.nodes;
13987         startIndex = startIndex || 0;
13988         endIndex = endIndex || ns.length - 1;
13989         for(var i = startIndex; i <= endIndex; i++){
13990             ns[i].nodeIndex = i;
13991         }
13992     },
13993
13994     /**
13995      * Changes the data store this view uses and refresh the view.
13996      * @param {Store} store
13997      */
13998     setStore : function(store, initial){
13999         if(!initial && this.store){
14000             this.store.un("datachanged", this.refresh);
14001             this.store.un("add", this.onAdd);
14002             this.store.un("remove", this.onRemove);
14003             this.store.un("update", this.onUpdate);
14004             this.store.un("clear", this.refresh);
14005             this.store.un("beforeload", this.onBeforeLoad);
14006             this.store.un("load", this.onLoad);
14007             this.store.un("loadexception", this.onLoad);
14008         }
14009         if(store){
14010           
14011             store.on("datachanged", this.refresh, this);
14012             store.on("add", this.onAdd, this);
14013             store.on("remove", this.onRemove, this);
14014             store.on("update", this.onUpdate, this);
14015             store.on("clear", this.refresh, this);
14016             store.on("beforeload", this.onBeforeLoad, this);
14017             store.on("load", this.onLoad, this);
14018             store.on("loadexception", this.onLoad, this);
14019         }
14020         
14021         if(store){
14022             this.refresh();
14023         }
14024     },
14025     /**
14026      * onbeforeLoad - masks the loading area.
14027      *
14028      */
14029     onBeforeLoad : function(store,opts)
14030     {
14031          //Roo.log('onBeforeLoad');   
14032         if (!opts.add) {
14033             this.el.update("");
14034         }
14035         this.el.mask(this.mask ? this.mask : "Loading" ); 
14036     },
14037     onLoad : function ()
14038     {
14039         this.el.unmask();
14040     },
14041     
14042
14043     /**
14044      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14045      * @param {HTMLElement} node
14046      * @return {HTMLElement} The template node
14047      */
14048     findItemFromChild : function(node){
14049         var el = this.dataName  ?
14050             this.el.child('.roo-tpl-' + this.dataName,true) :
14051             this.el.dom; 
14052         
14053         if(!node || node.parentNode == el){
14054                     return node;
14055             }
14056             var p = node.parentNode;
14057             while(p && p != el){
14058             if(p.parentNode == el){
14059                 return p;
14060             }
14061             p = p.parentNode;
14062         }
14063             return null;
14064     },
14065
14066     /** @ignore */
14067     onClick : function(e){
14068         var item = this.findItemFromChild(e.getTarget());
14069         if(item){
14070             var index = this.indexOf(item);
14071             if(this.onItemClick(item, index, e) !== false){
14072                 this.fireEvent("click", this, index, item, e);
14073             }
14074         }else{
14075             this.clearSelections();
14076         }
14077     },
14078
14079     /** @ignore */
14080     onContextMenu : function(e){
14081         var item = this.findItemFromChild(e.getTarget());
14082         if(item){
14083             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14084         }
14085     },
14086
14087     /** @ignore */
14088     onDblClick : function(e){
14089         var item = this.findItemFromChild(e.getTarget());
14090         if(item){
14091             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14092         }
14093     },
14094
14095     onItemClick : function(item, index, e)
14096     {
14097         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14098             return false;
14099         }
14100         if (this.toggleSelect) {
14101             var m = this.isSelected(item) ? 'unselect' : 'select';
14102             //Roo.log(m);
14103             var _t = this;
14104             _t[m](item, true, false);
14105             return true;
14106         }
14107         if(this.multiSelect || this.singleSelect){
14108             if(this.multiSelect && e.shiftKey && this.lastSelection){
14109                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14110             }else{
14111                 this.select(item, this.multiSelect && e.ctrlKey);
14112                 this.lastSelection = item;
14113             }
14114             
14115             if(!this.tickable){
14116                 e.preventDefault();
14117             }
14118             
14119         }
14120         return true;
14121     },
14122
14123     /**
14124      * Get the number of selected nodes.
14125      * @return {Number}
14126      */
14127     getSelectionCount : function(){
14128         return this.selections.length;
14129     },
14130
14131     /**
14132      * Get the currently selected nodes.
14133      * @return {Array} An array of HTMLElements
14134      */
14135     getSelectedNodes : function(){
14136         return this.selections;
14137     },
14138
14139     /**
14140      * Get the indexes of the selected nodes.
14141      * @return {Array}
14142      */
14143     getSelectedIndexes : function(){
14144         var indexes = [], s = this.selections;
14145         for(var i = 0, len = s.length; i < len; i++){
14146             indexes.push(s[i].nodeIndex);
14147         }
14148         return indexes;
14149     },
14150
14151     /**
14152      * Clear all selections
14153      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14154      */
14155     clearSelections : function(suppressEvent){
14156         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14157             this.cmp.elements = this.selections;
14158             this.cmp.removeClass(this.selectedClass);
14159             this.selections = [];
14160             if(!suppressEvent){
14161                 this.fireEvent("selectionchange", this, this.selections);
14162             }
14163         }
14164     },
14165
14166     /**
14167      * Returns true if the passed node is selected
14168      * @param {HTMLElement/Number} node The node or node index
14169      * @return {Boolean}
14170      */
14171     isSelected : function(node){
14172         var s = this.selections;
14173         if(s.length < 1){
14174             return false;
14175         }
14176         node = this.getNode(node);
14177         return s.indexOf(node) !== -1;
14178     },
14179
14180     /**
14181      * Selects nodes.
14182      * @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
14183      * @param {Boolean} keepExisting (optional) true to keep existing selections
14184      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14185      */
14186     select : function(nodeInfo, keepExisting, suppressEvent){
14187         if(nodeInfo instanceof Array){
14188             if(!keepExisting){
14189                 this.clearSelections(true);
14190             }
14191             for(var i = 0, len = nodeInfo.length; i < len; i++){
14192                 this.select(nodeInfo[i], true, true);
14193             }
14194             return;
14195         } 
14196         var node = this.getNode(nodeInfo);
14197         if(!node || this.isSelected(node)){
14198             return; // already selected.
14199         }
14200         if(!keepExisting){
14201             this.clearSelections(true);
14202         }
14203         
14204         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14205             Roo.fly(node).addClass(this.selectedClass);
14206             this.selections.push(node);
14207             if(!suppressEvent){
14208                 this.fireEvent("selectionchange", this, this.selections);
14209             }
14210         }
14211         
14212         
14213     },
14214       /**
14215      * Unselects nodes.
14216      * @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
14217      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14218      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14219      */
14220     unselect : function(nodeInfo, keepExisting, suppressEvent)
14221     {
14222         if(nodeInfo instanceof Array){
14223             Roo.each(this.selections, function(s) {
14224                 this.unselect(s, nodeInfo);
14225             }, this);
14226             return;
14227         }
14228         var node = this.getNode(nodeInfo);
14229         if(!node || !this.isSelected(node)){
14230             //Roo.log("not selected");
14231             return; // not selected.
14232         }
14233         // fireevent???
14234         var ns = [];
14235         Roo.each(this.selections, function(s) {
14236             if (s == node ) {
14237                 Roo.fly(node).removeClass(this.selectedClass);
14238
14239                 return;
14240             }
14241             ns.push(s);
14242         },this);
14243         
14244         this.selections= ns;
14245         this.fireEvent("selectionchange", this, this.selections);
14246     },
14247
14248     /**
14249      * Gets a template node.
14250      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14251      * @return {HTMLElement} The node or null if it wasn't found
14252      */
14253     getNode : function(nodeInfo){
14254         if(typeof nodeInfo == "string"){
14255             return document.getElementById(nodeInfo);
14256         }else if(typeof nodeInfo == "number"){
14257             return this.nodes[nodeInfo];
14258         }
14259         return nodeInfo;
14260     },
14261
14262     /**
14263      * Gets a range template nodes.
14264      * @param {Number} startIndex
14265      * @param {Number} endIndex
14266      * @return {Array} An array of nodes
14267      */
14268     getNodes : function(start, end){
14269         var ns = this.nodes;
14270         start = start || 0;
14271         end = typeof end == "undefined" ? ns.length - 1 : end;
14272         var nodes = [];
14273         if(start <= end){
14274             for(var i = start; i <= end; i++){
14275                 nodes.push(ns[i]);
14276             }
14277         } else{
14278             for(var i = start; i >= end; i--){
14279                 nodes.push(ns[i]);
14280             }
14281         }
14282         return nodes;
14283     },
14284
14285     /**
14286      * Finds the index of the passed node
14287      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14288      * @return {Number} The index of the node or -1
14289      */
14290     indexOf : function(node){
14291         node = this.getNode(node);
14292         if(typeof node.nodeIndex == "number"){
14293             return node.nodeIndex;
14294         }
14295         var ns = this.nodes;
14296         for(var i = 0, len = ns.length; i < len; i++){
14297             if(ns[i] == node){
14298                 return i;
14299             }
14300         }
14301         return -1;
14302     }
14303 });
14304 /*
14305  * - LGPL
14306  *
14307  * based on jquery fullcalendar
14308  * 
14309  */
14310
14311 Roo.bootstrap = Roo.bootstrap || {};
14312 /**
14313  * @class Roo.bootstrap.Calendar
14314  * @extends Roo.bootstrap.Component
14315  * Bootstrap Calendar class
14316  * @cfg {Boolean} loadMask (true|false) default false
14317  * @cfg {Object} header generate the user specific header of the calendar, default false
14318
14319  * @constructor
14320  * Create a new Container
14321  * @param {Object} config The config object
14322  */
14323
14324
14325
14326 Roo.bootstrap.Calendar = function(config){
14327     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14328      this.addEvents({
14329         /**
14330              * @event select
14331              * Fires when a date is selected
14332              * @param {DatePicker} this
14333              * @param {Date} date The selected date
14334              */
14335         'select': true,
14336         /**
14337              * @event monthchange
14338              * Fires when the displayed month changes 
14339              * @param {DatePicker} this
14340              * @param {Date} date The selected month
14341              */
14342         'monthchange': true,
14343         /**
14344              * @event evententer
14345              * Fires when mouse over an event
14346              * @param {Calendar} this
14347              * @param {event} Event
14348              */
14349         'evententer': true,
14350         /**
14351              * @event eventleave
14352              * Fires when the mouse leaves an
14353              * @param {Calendar} this
14354              * @param {event}
14355              */
14356         'eventleave': true,
14357         /**
14358              * @event eventclick
14359              * Fires when the mouse click an
14360              * @param {Calendar} this
14361              * @param {event}
14362              */
14363         'eventclick': true
14364         
14365     });
14366
14367 };
14368
14369 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14370     
14371      /**
14372      * @cfg {Number} startDay
14373      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14374      */
14375     startDay : 0,
14376     
14377     loadMask : false,
14378     
14379     header : false,
14380       
14381     getAutoCreate : function(){
14382         
14383         
14384         var fc_button = function(name, corner, style, content ) {
14385             return Roo.apply({},{
14386                 tag : 'span',
14387                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14388                          (corner.length ?
14389                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14390                             ''
14391                         ),
14392                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14393                 unselectable: 'on'
14394             });
14395         };
14396         
14397         var header = {};
14398         
14399         if(!this.header){
14400             header = {
14401                 tag : 'table',
14402                 cls : 'fc-header',
14403                 style : 'width:100%',
14404                 cn : [
14405                     {
14406                         tag: 'tr',
14407                         cn : [
14408                             {
14409                                 tag : 'td',
14410                                 cls : 'fc-header-left',
14411                                 cn : [
14412                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14413                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14414                                     { tag: 'span', cls: 'fc-header-space' },
14415                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14416
14417
14418                                 ]
14419                             },
14420
14421                             {
14422                                 tag : 'td',
14423                                 cls : 'fc-header-center',
14424                                 cn : [
14425                                     {
14426                                         tag: 'span',
14427                                         cls: 'fc-header-title',
14428                                         cn : {
14429                                             tag: 'H2',
14430                                             html : 'month / year'
14431                                         }
14432                                     }
14433
14434                                 ]
14435                             },
14436                             {
14437                                 tag : 'td',
14438                                 cls : 'fc-header-right',
14439                                 cn : [
14440                               /*      fc_button('month', 'left', '', 'month' ),
14441                                     fc_button('week', '', '', 'week' ),
14442                                     fc_button('day', 'right', '', 'day' )
14443                                 */    
14444
14445                                 ]
14446                             }
14447
14448                         ]
14449                     }
14450                 ]
14451             };
14452         }
14453         
14454         header = this.header;
14455         
14456        
14457         var cal_heads = function() {
14458             var ret = [];
14459             // fixme - handle this.
14460             
14461             for (var i =0; i < Date.dayNames.length; i++) {
14462                 var d = Date.dayNames[i];
14463                 ret.push({
14464                     tag: 'th',
14465                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14466                     html : d.substring(0,3)
14467                 });
14468                 
14469             }
14470             ret[0].cls += ' fc-first';
14471             ret[6].cls += ' fc-last';
14472             return ret;
14473         };
14474         var cal_cell = function(n) {
14475             return  {
14476                 tag: 'td',
14477                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14478                 cn : [
14479                     {
14480                         cn : [
14481                             {
14482                                 cls: 'fc-day-number',
14483                                 html: 'D'
14484                             },
14485                             {
14486                                 cls: 'fc-day-content',
14487                              
14488                                 cn : [
14489                                      {
14490                                         style: 'position: relative;' // height: 17px;
14491                                     }
14492                                 ]
14493                             }
14494                             
14495                             
14496                         ]
14497                     }
14498                 ]
14499                 
14500             }
14501         };
14502         var cal_rows = function() {
14503             
14504             var ret = [];
14505             for (var r = 0; r < 6; r++) {
14506                 var row= {
14507                     tag : 'tr',
14508                     cls : 'fc-week',
14509                     cn : []
14510                 };
14511                 
14512                 for (var i =0; i < Date.dayNames.length; i++) {
14513                     var d = Date.dayNames[i];
14514                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14515
14516                 }
14517                 row.cn[0].cls+=' fc-first';
14518                 row.cn[0].cn[0].style = 'min-height:90px';
14519                 row.cn[6].cls+=' fc-last';
14520                 ret.push(row);
14521                 
14522             }
14523             ret[0].cls += ' fc-first';
14524             ret[4].cls += ' fc-prev-last';
14525             ret[5].cls += ' fc-last';
14526             return ret;
14527             
14528         };
14529         
14530         var cal_table = {
14531             tag: 'table',
14532             cls: 'fc-border-separate',
14533             style : 'width:100%',
14534             cellspacing  : 0,
14535             cn : [
14536                 { 
14537                     tag: 'thead',
14538                     cn : [
14539                         { 
14540                             tag: 'tr',
14541                             cls : 'fc-first fc-last',
14542                             cn : cal_heads()
14543                         }
14544                     ]
14545                 },
14546                 { 
14547                     tag: 'tbody',
14548                     cn : cal_rows()
14549                 }
14550                   
14551             ]
14552         };
14553          
14554          var cfg = {
14555             cls : 'fc fc-ltr',
14556             cn : [
14557                 header,
14558                 {
14559                     cls : 'fc-content',
14560                     style : "position: relative;",
14561                     cn : [
14562                         {
14563                             cls : 'fc-view fc-view-month fc-grid',
14564                             style : 'position: relative',
14565                             unselectable : 'on',
14566                             cn : [
14567                                 {
14568                                     cls : 'fc-event-container',
14569                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14570                                 },
14571                                 cal_table
14572                             ]
14573                         }
14574                     ]
14575     
14576                 }
14577            ] 
14578             
14579         };
14580         
14581          
14582         
14583         return cfg;
14584     },
14585     
14586     
14587     initEvents : function()
14588     {
14589         if(!this.store){
14590             throw "can not find store for calendar";
14591         }
14592         
14593         var mark = {
14594             tag: "div",
14595             cls:"x-dlg-mask",
14596             style: "text-align:center",
14597             cn: [
14598                 {
14599                     tag: "div",
14600                     style: "background-color:white;width:50%;margin:250 auto",
14601                     cn: [
14602                         {
14603                             tag: "img",
14604                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14605                         },
14606                         {
14607                             tag: "span",
14608                             html: "Loading"
14609                         }
14610                         
14611                     ]
14612                 }
14613             ]
14614         }
14615         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14616         
14617         var size = this.el.select('.fc-content', true).first().getSize();
14618         this.maskEl.setSize(size.width, size.height);
14619         this.maskEl.enableDisplayMode("block");
14620         if(!this.loadMask){
14621             this.maskEl.hide();
14622         }
14623         
14624         this.store = Roo.factory(this.store, Roo.data);
14625         this.store.on('load', this.onLoad, this);
14626         this.store.on('beforeload', this.onBeforeLoad, this);
14627         
14628         this.resize();
14629         
14630         this.cells = this.el.select('.fc-day',true);
14631         //Roo.log(this.cells);
14632         this.textNodes = this.el.query('.fc-day-number');
14633         this.cells.addClassOnOver('fc-state-hover');
14634         
14635         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14636         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14637         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14638         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14639         
14640         this.on('monthchange', this.onMonthChange, this);
14641         
14642         this.update(new Date().clearTime());
14643     },
14644     
14645     resize : function() {
14646         var sz  = this.el.getSize();
14647         
14648         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14649         this.el.select('.fc-day-content div',true).setHeight(34);
14650     },
14651     
14652     
14653     // private
14654     showPrevMonth : function(e){
14655         this.update(this.activeDate.add("mo", -1));
14656     },
14657     showToday : function(e){
14658         this.update(new Date().clearTime());
14659     },
14660     // private
14661     showNextMonth : function(e){
14662         this.update(this.activeDate.add("mo", 1));
14663     },
14664
14665     // private
14666     showPrevYear : function(){
14667         this.update(this.activeDate.add("y", -1));
14668     },
14669
14670     // private
14671     showNextYear : function(){
14672         this.update(this.activeDate.add("y", 1));
14673     },
14674
14675     
14676    // private
14677     update : function(date)
14678     {
14679         var vd = this.activeDate;
14680         this.activeDate = date;
14681 //        if(vd && this.el){
14682 //            var t = date.getTime();
14683 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14684 //                Roo.log('using add remove');
14685 //                
14686 //                this.fireEvent('monthchange', this, date);
14687 //                
14688 //                this.cells.removeClass("fc-state-highlight");
14689 //                this.cells.each(function(c){
14690 //                   if(c.dateValue == t){
14691 //                       c.addClass("fc-state-highlight");
14692 //                       setTimeout(function(){
14693 //                            try{c.dom.firstChild.focus();}catch(e){}
14694 //                       }, 50);
14695 //                       return false;
14696 //                   }
14697 //                   return true;
14698 //                });
14699 //                return;
14700 //            }
14701 //        }
14702         
14703         var days = date.getDaysInMonth();
14704         
14705         var firstOfMonth = date.getFirstDateOfMonth();
14706         var startingPos = firstOfMonth.getDay()-this.startDay;
14707         
14708         if(startingPos < this.startDay){
14709             startingPos += 7;
14710         }
14711         
14712         var pm = date.add(Date.MONTH, -1);
14713         var prevStart = pm.getDaysInMonth()-startingPos;
14714 //        
14715         this.cells = this.el.select('.fc-day',true);
14716         this.textNodes = this.el.query('.fc-day-number');
14717         this.cells.addClassOnOver('fc-state-hover');
14718         
14719         var cells = this.cells.elements;
14720         var textEls = this.textNodes;
14721         
14722         Roo.each(cells, function(cell){
14723             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14724         });
14725         
14726         days += startingPos;
14727
14728         // convert everything to numbers so it's fast
14729         var day = 86400000;
14730         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14731         //Roo.log(d);
14732         //Roo.log(pm);
14733         //Roo.log(prevStart);
14734         
14735         var today = new Date().clearTime().getTime();
14736         var sel = date.clearTime().getTime();
14737         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14738         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14739         var ddMatch = this.disabledDatesRE;
14740         var ddText = this.disabledDatesText;
14741         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14742         var ddaysText = this.disabledDaysText;
14743         var format = this.format;
14744         
14745         var setCellClass = function(cal, cell){
14746             cell.row = 0;
14747             cell.events = [];
14748             cell.more = [];
14749             //Roo.log('set Cell Class');
14750             cell.title = "";
14751             var t = d.getTime();
14752             
14753             //Roo.log(d);
14754             
14755             cell.dateValue = t;
14756             if(t == today){
14757                 cell.className += " fc-today";
14758                 cell.className += " fc-state-highlight";
14759                 cell.title = cal.todayText;
14760             }
14761             if(t == sel){
14762                 // disable highlight in other month..
14763                 //cell.className += " fc-state-highlight";
14764                 
14765             }
14766             // disabling
14767             if(t < min) {
14768                 cell.className = " fc-state-disabled";
14769                 cell.title = cal.minText;
14770                 return;
14771             }
14772             if(t > max) {
14773                 cell.className = " fc-state-disabled";
14774                 cell.title = cal.maxText;
14775                 return;
14776             }
14777             if(ddays){
14778                 if(ddays.indexOf(d.getDay()) != -1){
14779                     cell.title = ddaysText;
14780                     cell.className = " fc-state-disabled";
14781                 }
14782             }
14783             if(ddMatch && format){
14784                 var fvalue = d.dateFormat(format);
14785                 if(ddMatch.test(fvalue)){
14786                     cell.title = ddText.replace("%0", fvalue);
14787                     cell.className = " fc-state-disabled";
14788                 }
14789             }
14790             
14791             if (!cell.initialClassName) {
14792                 cell.initialClassName = cell.dom.className;
14793             }
14794             
14795             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14796         };
14797
14798         var i = 0;
14799         
14800         for(; i < startingPos; i++) {
14801             textEls[i].innerHTML = (++prevStart);
14802             d.setDate(d.getDate()+1);
14803             
14804             cells[i].className = "fc-past fc-other-month";
14805             setCellClass(this, cells[i]);
14806         }
14807         
14808         var intDay = 0;
14809         
14810         for(; i < days; i++){
14811             intDay = i - startingPos + 1;
14812             textEls[i].innerHTML = (intDay);
14813             d.setDate(d.getDate()+1);
14814             
14815             cells[i].className = ''; // "x-date-active";
14816             setCellClass(this, cells[i]);
14817         }
14818         var extraDays = 0;
14819         
14820         for(; i < 42; i++) {
14821             textEls[i].innerHTML = (++extraDays);
14822             d.setDate(d.getDate()+1);
14823             
14824             cells[i].className = "fc-future fc-other-month";
14825             setCellClass(this, cells[i]);
14826         }
14827         
14828         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14829         
14830         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14831         
14832         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14833         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14834         
14835         if(totalRows != 6){
14836             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14837             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14838         }
14839         
14840         this.fireEvent('monthchange', this, date);
14841         
14842         
14843         /*
14844         if(!this.internalRender){
14845             var main = this.el.dom.firstChild;
14846             var w = main.offsetWidth;
14847             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14848             Roo.fly(main).setWidth(w);
14849             this.internalRender = true;
14850             // opera does not respect the auto grow header center column
14851             // then, after it gets a width opera refuses to recalculate
14852             // without a second pass
14853             if(Roo.isOpera && !this.secondPass){
14854                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14855                 this.secondPass = true;
14856                 this.update.defer(10, this, [date]);
14857             }
14858         }
14859         */
14860         
14861     },
14862     
14863     findCell : function(dt) {
14864         dt = dt.clearTime().getTime();
14865         var ret = false;
14866         this.cells.each(function(c){
14867             //Roo.log("check " +c.dateValue + '?=' + dt);
14868             if(c.dateValue == dt){
14869                 ret = c;
14870                 return false;
14871             }
14872             return true;
14873         });
14874         
14875         return ret;
14876     },
14877     
14878     findCells : function(ev) {
14879         var s = ev.start.clone().clearTime().getTime();
14880        // Roo.log(s);
14881         var e= ev.end.clone().clearTime().getTime();
14882        // Roo.log(e);
14883         var ret = [];
14884         this.cells.each(function(c){
14885              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14886             
14887             if(c.dateValue > e){
14888                 return ;
14889             }
14890             if(c.dateValue < s){
14891                 return ;
14892             }
14893             ret.push(c);
14894         });
14895         
14896         return ret;    
14897     },
14898     
14899 //    findBestRow: function(cells)
14900 //    {
14901 //        var ret = 0;
14902 //        
14903 //        for (var i =0 ; i < cells.length;i++) {
14904 //            ret  = Math.max(cells[i].rows || 0,ret);
14905 //        }
14906 //        return ret;
14907 //        
14908 //    },
14909     
14910     
14911     addItem : function(ev)
14912     {
14913         // look for vertical location slot in
14914         var cells = this.findCells(ev);
14915         
14916 //        ev.row = this.findBestRow(cells);
14917         
14918         // work out the location.
14919         
14920         var crow = false;
14921         var rows = [];
14922         for(var i =0; i < cells.length; i++) {
14923             
14924             cells[i].row = cells[0].row;
14925             
14926             if(i == 0){
14927                 cells[i].row = cells[i].row + 1;
14928             }
14929             
14930             if (!crow) {
14931                 crow = {
14932                     start : cells[i],
14933                     end :  cells[i]
14934                 };
14935                 continue;
14936             }
14937             if (crow.start.getY() == cells[i].getY()) {
14938                 // on same row.
14939                 crow.end = cells[i];
14940                 continue;
14941             }
14942             // different row.
14943             rows.push(crow);
14944             crow = {
14945                 start: cells[i],
14946                 end : cells[i]
14947             };
14948             
14949         }
14950         
14951         rows.push(crow);
14952         ev.els = [];
14953         ev.rows = rows;
14954         ev.cells = cells;
14955         
14956         cells[0].events.push(ev);
14957         
14958         this.calevents.push(ev);
14959     },
14960     
14961     clearEvents: function() {
14962         
14963         if(!this.calevents){
14964             return;
14965         }
14966         
14967         Roo.each(this.cells.elements, function(c){
14968             c.row = 0;
14969             c.events = [];
14970             c.more = [];
14971         });
14972         
14973         Roo.each(this.calevents, function(e) {
14974             Roo.each(e.els, function(el) {
14975                 el.un('mouseenter' ,this.onEventEnter, this);
14976                 el.un('mouseleave' ,this.onEventLeave, this);
14977                 el.remove();
14978             },this);
14979         },this);
14980         
14981         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14982             e.remove();
14983         });
14984         
14985     },
14986     
14987     renderEvents: function()
14988     {   
14989         var _this = this;
14990         
14991         this.cells.each(function(c) {
14992             
14993             if(c.row < 5){
14994                 return;
14995             }
14996             
14997             var ev = c.events;
14998             
14999             var r = 4;
15000             if(c.row != c.events.length){
15001                 r = 4 - (4 - (c.row - c.events.length));
15002             }
15003             
15004             c.events = ev.slice(0, r);
15005             c.more = ev.slice(r);
15006             
15007             if(c.more.length && c.more.length == 1){
15008                 c.events.push(c.more.pop());
15009             }
15010             
15011             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15012             
15013         });
15014             
15015         this.cells.each(function(c) {
15016             
15017             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15018             
15019             
15020             for (var e = 0; e < c.events.length; e++){
15021                 var ev = c.events[e];
15022                 var rows = ev.rows;
15023                 
15024                 for(var i = 0; i < rows.length; i++) {
15025                 
15026                     // how many rows should it span..
15027
15028                     var  cfg = {
15029                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15030                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15031
15032                         unselectable : "on",
15033                         cn : [
15034                             {
15035                                 cls: 'fc-event-inner',
15036                                 cn : [
15037     //                                {
15038     //                                  tag:'span',
15039     //                                  cls: 'fc-event-time',
15040     //                                  html : cells.length > 1 ? '' : ev.time
15041     //                                },
15042                                     {
15043                                       tag:'span',
15044                                       cls: 'fc-event-title',
15045                                       html : String.format('{0}', ev.title)
15046                                     }
15047
15048
15049                                 ]
15050                             },
15051                             {
15052                                 cls: 'ui-resizable-handle ui-resizable-e',
15053                                 html : '&nbsp;&nbsp;&nbsp'
15054                             }
15055
15056                         ]
15057                     };
15058
15059                     if (i == 0) {
15060                         cfg.cls += ' fc-event-start';
15061                     }
15062                     if ((i+1) == rows.length) {
15063                         cfg.cls += ' fc-event-end';
15064                     }
15065
15066                     var ctr = _this.el.select('.fc-event-container',true).first();
15067                     var cg = ctr.createChild(cfg);
15068
15069                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15070                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15071
15072                     var r = (c.more.length) ? 1 : 0;
15073                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15074                     cg.setWidth(ebox.right - sbox.x -2);
15075
15076                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15077                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15078                     cg.on('click', _this.onEventClick, _this, ev);
15079
15080                     ev.els.push(cg);
15081                     
15082                 }
15083                 
15084             }
15085             
15086             
15087             if(c.more.length){
15088                 var  cfg = {
15089                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15090                     style : 'position: absolute',
15091                     unselectable : "on",
15092                     cn : [
15093                         {
15094                             cls: 'fc-event-inner',
15095                             cn : [
15096                                 {
15097                                   tag:'span',
15098                                   cls: 'fc-event-title',
15099                                   html : 'More'
15100                                 }
15101
15102
15103                             ]
15104                         },
15105                         {
15106                             cls: 'ui-resizable-handle ui-resizable-e',
15107                             html : '&nbsp;&nbsp;&nbsp'
15108                         }
15109
15110                     ]
15111                 };
15112
15113                 var ctr = _this.el.select('.fc-event-container',true).first();
15114                 var cg = ctr.createChild(cfg);
15115
15116                 var sbox = c.select('.fc-day-content',true).first().getBox();
15117                 var ebox = c.select('.fc-day-content',true).first().getBox();
15118                 //Roo.log(cg);
15119                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15120                 cg.setWidth(ebox.right - sbox.x -2);
15121
15122                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15123                 
15124             }
15125             
15126         });
15127         
15128         
15129         
15130     },
15131     
15132     onEventEnter: function (e, el,event,d) {
15133         this.fireEvent('evententer', this, el, event);
15134     },
15135     
15136     onEventLeave: function (e, el,event,d) {
15137         this.fireEvent('eventleave', this, el, event);
15138     },
15139     
15140     onEventClick: function (e, el,event,d) {
15141         this.fireEvent('eventclick', this, el, event);
15142     },
15143     
15144     onMonthChange: function () {
15145         this.store.load();
15146     },
15147     
15148     onMoreEventClick: function(e, el, more)
15149     {
15150         var _this = this;
15151         
15152         this.calpopover.placement = 'right';
15153         this.calpopover.setTitle('More');
15154         
15155         this.calpopover.setContent('');
15156         
15157         var ctr = this.calpopover.el.select('.popover-content', true).first();
15158         
15159         Roo.each(more, function(m){
15160             var cfg = {
15161                 cls : 'fc-event-hori fc-event-draggable',
15162                 html : m.title
15163             }
15164             var cg = ctr.createChild(cfg);
15165             
15166             cg.on('click', _this.onEventClick, _this, m);
15167         });
15168         
15169         this.calpopover.show(el);
15170         
15171         
15172     },
15173     
15174     onLoad: function () 
15175     {   
15176         this.calevents = [];
15177         var cal = this;
15178         
15179         if(this.store.getCount() > 0){
15180             this.store.data.each(function(d){
15181                cal.addItem({
15182                     id : d.data.id,
15183                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15184                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15185                     time : d.data.start_time,
15186                     title : d.data.title,
15187                     description : d.data.description,
15188                     venue : d.data.venue
15189                 });
15190             });
15191         }
15192         
15193         this.renderEvents();
15194         
15195         if(this.calevents.length && this.loadMask){
15196             this.maskEl.hide();
15197         }
15198     },
15199     
15200     onBeforeLoad: function()
15201     {
15202         this.clearEvents();
15203         if(this.loadMask){
15204             this.maskEl.show();
15205         }
15206     }
15207 });
15208
15209  
15210  /*
15211  * - LGPL
15212  *
15213  * element
15214  * 
15215  */
15216
15217 /**
15218  * @class Roo.bootstrap.Popover
15219  * @extends Roo.bootstrap.Component
15220  * Bootstrap Popover class
15221  * @cfg {String} html contents of the popover   (or false to use children..)
15222  * @cfg {String} title of popover (or false to hide)
15223  * @cfg {String} placement how it is placed
15224  * @cfg {String} trigger click || hover (or false to trigger manually)
15225  * @cfg {String} over what (parent or false to trigger manually.)
15226  * @cfg {Number} delay - delay before showing
15227  
15228  * @constructor
15229  * Create a new Popover
15230  * @param {Object} config The config object
15231  */
15232
15233 Roo.bootstrap.Popover = function(config){
15234     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15235 };
15236
15237 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15238     
15239     title: 'Fill in a title',
15240     html: false,
15241     
15242     placement : 'right',
15243     trigger : 'hover', // hover
15244     
15245     delay : 0,
15246     
15247     over: 'parent',
15248     
15249     can_build_overlaid : false,
15250     
15251     getChildContainer : function()
15252     {
15253         return this.el.select('.popover-content',true).first();
15254     },
15255     
15256     getAutoCreate : function(){
15257          Roo.log('make popover?');
15258         var cfg = {
15259            cls : 'popover roo-dynamic',
15260            style: 'display:block',
15261            cn : [
15262                 {
15263                     cls : 'arrow'
15264                 },
15265                 {
15266                     cls : 'popover-inner',
15267                     cn : [
15268                         {
15269                             tag: 'h3',
15270                             cls: 'popover-title',
15271                             html : this.title
15272                         },
15273                         {
15274                             cls : 'popover-content',
15275                             html : this.html
15276                         }
15277                     ]
15278                     
15279                 }
15280            ]
15281         };
15282         
15283         return cfg;
15284     },
15285     setTitle: function(str)
15286     {
15287         this.title = str;
15288         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15289     },
15290     setContent: function(str)
15291     {
15292         this.html = str;
15293         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15294     },
15295     // as it get's added to the bottom of the page.
15296     onRender : function(ct, position)
15297     {
15298         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15299         if(!this.el){
15300             var cfg = Roo.apply({},  this.getAutoCreate());
15301             cfg.id = Roo.id();
15302             
15303             if (this.cls) {
15304                 cfg.cls += ' ' + this.cls;
15305             }
15306             if (this.style) {
15307                 cfg.style = this.style;
15308             }
15309             Roo.log("adding to ")
15310             this.el = Roo.get(document.body).createChild(cfg, position);
15311             Roo.log(this.el);
15312         }
15313         this.initEvents();
15314     },
15315     
15316     initEvents : function()
15317     {
15318         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15319         this.el.enableDisplayMode('block');
15320         this.el.hide();
15321         if (this.over === false) {
15322             return; 
15323         }
15324         if (this.triggers === false) {
15325             return;
15326         }
15327         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15328         var triggers = this.trigger ? this.trigger.split(' ') : [];
15329         Roo.each(triggers, function(trigger) {
15330         
15331             if (trigger == 'click') {
15332                 on_el.on('click', this.toggle, this);
15333             } else if (trigger != 'manual') {
15334                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15335                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15336       
15337                 on_el.on(eventIn  ,this.enter, this);
15338                 on_el.on(eventOut, this.leave, this);
15339             }
15340         }, this);
15341         
15342     },
15343     
15344     
15345     // private
15346     timeout : null,
15347     hoverState : null,
15348     
15349     toggle : function () {
15350         this.hoverState == 'in' ? this.leave() : this.enter();
15351     },
15352     
15353     enter : function () {
15354        
15355     
15356         clearTimeout(this.timeout);
15357     
15358         this.hoverState = 'in';
15359     
15360         if (!this.delay || !this.delay.show) {
15361             this.show();
15362             return;
15363         }
15364         var _t = this;
15365         this.timeout = setTimeout(function () {
15366             if (_t.hoverState == 'in') {
15367                 _t.show();
15368             }
15369         }, this.delay.show)
15370     },
15371     leave : function() {
15372         clearTimeout(this.timeout);
15373     
15374         this.hoverState = 'out';
15375     
15376         if (!this.delay || !this.delay.hide) {
15377             this.hide();
15378             return;
15379         }
15380         var _t = this;
15381         this.timeout = setTimeout(function () {
15382             if (_t.hoverState == 'out') {
15383                 _t.hide();
15384             }
15385         }, this.delay.hide)
15386     },
15387     
15388     show : function (on_el)
15389     {
15390         if (!on_el) {
15391             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15392         }
15393         // set content.
15394         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15395         if (this.html !== false) {
15396             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15397         }
15398         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15399         if (!this.title.length) {
15400             this.el.select('.popover-title',true).hide();
15401         }
15402         
15403         var placement = typeof this.placement == 'function' ?
15404             this.placement.call(this, this.el, on_el) :
15405             this.placement;
15406             
15407         var autoToken = /\s?auto?\s?/i;
15408         var autoPlace = autoToken.test(placement);
15409         if (autoPlace) {
15410             placement = placement.replace(autoToken, '') || 'top';
15411         }
15412         
15413         //this.el.detach()
15414         //this.el.setXY([0,0]);
15415         this.el.show();
15416         this.el.dom.style.display='block';
15417         this.el.addClass(placement);
15418         
15419         //this.el.appendTo(on_el);
15420         
15421         var p = this.getPosition();
15422         var box = this.el.getBox();
15423         
15424         if (autoPlace) {
15425             // fixme..
15426         }
15427         var align = Roo.bootstrap.Popover.alignment[placement];
15428         this.el.alignTo(on_el, align[0],align[1]);
15429         //var arrow = this.el.select('.arrow',true).first();
15430         //arrow.set(align[2], 
15431         
15432         this.el.addClass('in');
15433         
15434         
15435         if (this.el.hasClass('fade')) {
15436             // fade it?
15437         }
15438         
15439     },
15440     hide : function()
15441     {
15442         this.el.setXY([0,0]);
15443         this.el.removeClass('in');
15444         this.el.hide();
15445         this.hoverState = null;
15446         
15447     }
15448     
15449 });
15450
15451 Roo.bootstrap.Popover.alignment = {
15452     'left' : ['r-l', [-10,0], 'right'],
15453     'right' : ['l-r', [10,0], 'left'],
15454     'bottom' : ['t-b', [0,10], 'top'],
15455     'top' : [ 'b-t', [0,-10], 'bottom']
15456 };
15457
15458  /*
15459  * - LGPL
15460  *
15461  * Progress
15462  * 
15463  */
15464
15465 /**
15466  * @class Roo.bootstrap.Progress
15467  * @extends Roo.bootstrap.Component
15468  * Bootstrap Progress class
15469  * @cfg {Boolean} striped striped of the progress bar
15470  * @cfg {Boolean} active animated of the progress bar
15471  * 
15472  * 
15473  * @constructor
15474  * Create a new Progress
15475  * @param {Object} config The config object
15476  */
15477
15478 Roo.bootstrap.Progress = function(config){
15479     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15480 };
15481
15482 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15483     
15484     striped : false,
15485     active: false,
15486     
15487     getAutoCreate : function(){
15488         var cfg = {
15489             tag: 'div',
15490             cls: 'progress'
15491         };
15492         
15493         
15494         if(this.striped){
15495             cfg.cls += ' progress-striped';
15496         }
15497       
15498         if(this.active){
15499             cfg.cls += ' active';
15500         }
15501         
15502         
15503         return cfg;
15504     }
15505    
15506 });
15507
15508  
15509
15510  /*
15511  * - LGPL
15512  *
15513  * ProgressBar
15514  * 
15515  */
15516
15517 /**
15518  * @class Roo.bootstrap.ProgressBar
15519  * @extends Roo.bootstrap.Component
15520  * Bootstrap ProgressBar class
15521  * @cfg {Number} aria_valuenow aria-value now
15522  * @cfg {Number} aria_valuemin aria-value min
15523  * @cfg {Number} aria_valuemax aria-value max
15524  * @cfg {String} label label for the progress bar
15525  * @cfg {String} panel (success | info | warning | danger )
15526  * @cfg {String} role role of the progress bar
15527  * @cfg {String} sr_only text
15528  * 
15529  * 
15530  * @constructor
15531  * Create a new ProgressBar
15532  * @param {Object} config The config object
15533  */
15534
15535 Roo.bootstrap.ProgressBar = function(config){
15536     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15537 };
15538
15539 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15540     
15541     aria_valuenow : 0,
15542     aria_valuemin : 0,
15543     aria_valuemax : 100,
15544     label : false,
15545     panel : false,
15546     role : false,
15547     sr_only: false,
15548     
15549     getAutoCreate : function()
15550     {
15551         
15552         var cfg = {
15553             tag: 'div',
15554             cls: 'progress-bar',
15555             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15556         };
15557         
15558         if(this.sr_only){
15559             cfg.cn = {
15560                 tag: 'span',
15561                 cls: 'sr-only',
15562                 html: this.sr_only
15563             }
15564         }
15565         
15566         if(this.role){
15567             cfg.role = this.role;
15568         }
15569         
15570         if(this.aria_valuenow){
15571             cfg['aria-valuenow'] = this.aria_valuenow;
15572         }
15573         
15574         if(this.aria_valuemin){
15575             cfg['aria-valuemin'] = this.aria_valuemin;
15576         }
15577         
15578         if(this.aria_valuemax){
15579             cfg['aria-valuemax'] = this.aria_valuemax;
15580         }
15581         
15582         if(this.label && !this.sr_only){
15583             cfg.html = this.label;
15584         }
15585         
15586         if(this.panel){
15587             cfg.cls += ' progress-bar-' + this.panel;
15588         }
15589         
15590         return cfg;
15591     },
15592     
15593     update : function(aria_valuenow)
15594     {
15595         this.aria_valuenow = aria_valuenow;
15596         
15597         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15598     }
15599    
15600 });
15601
15602  
15603
15604  /*
15605  * - LGPL
15606  *
15607  * column
15608  * 
15609  */
15610
15611 /**
15612  * @class Roo.bootstrap.TabGroup
15613  * @extends Roo.bootstrap.Column
15614  * Bootstrap Column class
15615  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15616  * @cfg {Boolean} carousel true to make the group behave like a carousel
15617  * @cfg {Number} bullets show the panel pointer.. default 0
15618  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15619  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15620  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15621  * 
15622  * @constructor
15623  * Create a new TabGroup
15624  * @param {Object} config The config object
15625  */
15626
15627 Roo.bootstrap.TabGroup = function(config){
15628     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15629     if (!this.navId) {
15630         this.navId = Roo.id();
15631     }
15632     this.tabs = [];
15633     Roo.bootstrap.TabGroup.register(this);
15634     
15635 };
15636
15637 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15638     
15639     carousel : false,
15640     transition : false,
15641     bullets : 0,
15642     timer : 0,
15643     autoslide : false,
15644     slideFn : false,
15645     slideOnTouch : false,
15646     
15647     getAutoCreate : function()
15648     {
15649         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15650         
15651         cfg.cls += ' tab-content';
15652         
15653         Roo.log('get auto create...............');
15654         
15655         if (this.carousel) {
15656             cfg.cls += ' carousel slide';
15657             
15658             cfg.cn = [{
15659                cls : 'carousel-inner'
15660             }];
15661         
15662             if(this.bullets > 0 && !Roo.isTouch){
15663                 
15664                 var bullets = {
15665                     cls : 'carousel-bullets',
15666                     cn : []
15667                 };
15668                 
15669                 if(this.bullets_cls){
15670                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15671                 }
15672                 
15673                 for (var i = 0; i < this.bullets; i++){
15674                     bullets.cn.push({
15675                         cls : 'bullet bullet-' + i
15676                     });
15677                 }
15678                 
15679                 bullets.cn.push({
15680                     cls : 'clear'
15681                 });
15682                 
15683                 cfg.cn[0].cn = bullets;
15684             }
15685         }
15686         
15687         return cfg;
15688     },
15689     
15690     initEvents:  function()
15691     {
15692         Roo.log('-------- init events on tab group ---------');
15693         
15694         if(this.bullets > 0 && !Roo.isTouch){
15695             this.initBullet();
15696         }
15697         
15698         Roo.log(this);
15699         
15700         if(Roo.isTouch && this.slideOnTouch){
15701             this.el.on("touchstart", this.onTouchStart, this);
15702         }
15703         
15704         if(this.autoslide){
15705             var _this = this;
15706             
15707             this.slideFn = window.setInterval(function() {
15708                 _this.showPanelNext();
15709             }, this.timer);
15710         }
15711         
15712     },
15713     
15714     onTouchStart : function(e, el, o)
15715     {
15716         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15717             return;
15718         }
15719         
15720         this.showPanelNext();
15721     },
15722     
15723     getChildContainer : function()
15724     {
15725         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15726     },
15727     
15728     /**
15729     * register a Navigation item
15730     * @param {Roo.bootstrap.NavItem} the navitem to add
15731     */
15732     register : function(item)
15733     {
15734         this.tabs.push( item);
15735         item.navId = this.navId; // not really needed..
15736     
15737     },
15738     
15739     getActivePanel : function()
15740     {
15741         var r = false;
15742         Roo.each(this.tabs, function(t) {
15743             if (t.active) {
15744                 r = t;
15745                 return false;
15746             }
15747             return null;
15748         });
15749         return r;
15750         
15751     },
15752     getPanelByName : function(n)
15753     {
15754         var r = false;
15755         Roo.each(this.tabs, function(t) {
15756             if (t.tabId == n) {
15757                 r = t;
15758                 return false;
15759             }
15760             return null;
15761         });
15762         return r;
15763     },
15764     indexOfPanel : function(p)
15765     {
15766         var r = false;
15767         Roo.each(this.tabs, function(t,i) {
15768             if (t.tabId == p.tabId) {
15769                 r = i;
15770                 return false;
15771             }
15772             return null;
15773         });
15774         return r;
15775     },
15776     /**
15777      * show a specific panel
15778      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15779      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15780      */
15781     showPanel : function (pan)
15782     {
15783         if(this.transition){
15784             Roo.log("waiting for the transitionend");
15785             return;
15786         }
15787         
15788         if (typeof(pan) == 'number') {
15789             pan = this.tabs[pan];
15790         }
15791         if (typeof(pan) == 'string') {
15792             pan = this.getPanelByName(pan);
15793         }
15794         if (pan.tabId == this.getActivePanel().tabId) {
15795             return true;
15796         }
15797         var cur = this.getActivePanel();
15798         
15799         if (false === cur.fireEvent('beforedeactivate')) {
15800             return false;
15801         }
15802         
15803         if(this.bullets > 0 && !Roo.isTouch){
15804             this.setActiveBullet(this.indexOfPanel(pan));
15805         }
15806         
15807         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15808             
15809             this.transition = true;
15810             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15811             var lr = dir == 'next' ? 'left' : 'right';
15812             pan.el.addClass(dir); // or prev
15813             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15814             cur.el.addClass(lr); // or right
15815             pan.el.addClass(lr);
15816             
15817             var _this = this;
15818             cur.el.on('transitionend', function() {
15819                 Roo.log("trans end?");
15820                 
15821                 pan.el.removeClass([lr,dir]);
15822                 pan.setActive(true);
15823                 
15824                 cur.el.removeClass([lr]);
15825                 cur.setActive(false);
15826                 
15827                 _this.transition = false;
15828                 
15829             }, this, { single:  true } );
15830             
15831             return true;
15832         }
15833         
15834         cur.setActive(false);
15835         pan.setActive(true);
15836         
15837         return true;
15838         
15839     },
15840     showPanelNext : function()
15841     {
15842         var i = this.indexOfPanel(this.getActivePanel());
15843         
15844         if (i >= this.tabs.length - 1 && !this.autoslide) {
15845             return;
15846         }
15847         
15848         if (i >= this.tabs.length - 1 && this.autoslide) {
15849             i = -1;
15850         }
15851         
15852         this.showPanel(this.tabs[i+1]);
15853     },
15854     
15855     showPanelPrev : function()
15856     {
15857         var i = this.indexOfPanel(this.getActivePanel());
15858         
15859         if (i  < 1 && !this.autoslide) {
15860             return;
15861         }
15862         
15863         if (i < 1 && this.autoslide) {
15864             i = this.tabs.length;
15865         }
15866         
15867         this.showPanel(this.tabs[i-1]);
15868     },
15869     
15870     initBullet : function()
15871     {
15872         if(Roo.isTouch){
15873             return;
15874         }
15875         
15876         var _this = this;
15877         
15878         for (var i = 0; i < this.bullets; i++){
15879             var bullet = this.el.select('.bullet-' + i, true).first();
15880
15881             if(!bullet){
15882                 continue;
15883             }
15884
15885             bullet.on('click', (function(e, el, o, ii, t){
15886
15887                 e.preventDefault();
15888
15889                 _this.showPanel(ii);
15890
15891                 if(_this.autoslide && _this.slideFn){
15892                     clearInterval(_this.slideFn);
15893                     _this.slideFn = window.setInterval(function() {
15894                         _this.showPanelNext();
15895                     }, _this.timer);
15896                 }
15897
15898             }).createDelegate(this, [i, bullet], true));
15899         }
15900     },
15901     
15902     setActiveBullet : function(i)
15903     {
15904         if(Roo.isTouch){
15905             return;
15906         }
15907         
15908         Roo.each(this.el.select('.bullet', true).elements, function(el){
15909             el.removeClass('selected');
15910         });
15911
15912         var bullet = this.el.select('.bullet-' + i, true).first();
15913         
15914         if(!bullet){
15915             return;
15916         }
15917         
15918         bullet.addClass('selected');
15919     }
15920     
15921     
15922   
15923 });
15924
15925  
15926
15927  
15928  
15929 Roo.apply(Roo.bootstrap.TabGroup, {
15930     
15931     groups: {},
15932      /**
15933     * register a Navigation Group
15934     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15935     */
15936     register : function(navgrp)
15937     {
15938         this.groups[navgrp.navId] = navgrp;
15939         
15940     },
15941     /**
15942     * fetch a Navigation Group based on the navigation ID
15943     * if one does not exist , it will get created.
15944     * @param {string} the navgroup to add
15945     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15946     */
15947     get: function(navId) {
15948         if (typeof(this.groups[navId]) == 'undefined') {
15949             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15950         }
15951         return this.groups[navId] ;
15952     }
15953     
15954     
15955     
15956 });
15957
15958  /*
15959  * - LGPL
15960  *
15961  * TabPanel
15962  * 
15963  */
15964
15965 /**
15966  * @class Roo.bootstrap.TabPanel
15967  * @extends Roo.bootstrap.Component
15968  * Bootstrap TabPanel class
15969  * @cfg {Boolean} active panel active
15970  * @cfg {String} html panel content
15971  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15972  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15973  * 
15974  * 
15975  * @constructor
15976  * Create a new TabPanel
15977  * @param {Object} config The config object
15978  */
15979
15980 Roo.bootstrap.TabPanel = function(config){
15981     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15982     this.addEvents({
15983         /**
15984              * @event changed
15985              * Fires when the active status changes
15986              * @param {Roo.bootstrap.TabPanel} this
15987              * @param {Boolean} state the new state
15988             
15989          */
15990         'changed': true,
15991         /**
15992              * @event beforedeactivate
15993              * Fires before a tab is de-activated - can be used to do validation on a form.
15994              * @param {Roo.bootstrap.TabPanel} this
15995              * @return {Boolean} false if there is an error
15996             
15997          */
15998         'beforedeactivate': true
15999      });
16000     
16001     this.tabId = this.tabId || Roo.id();
16002   
16003 };
16004
16005 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16006     
16007     active: false,
16008     html: false,
16009     tabId: false,
16010     navId : false,
16011     
16012     getAutoCreate : function(){
16013         var cfg = {
16014             tag: 'div',
16015             // item is needed for carousel - not sure if it has any effect otherwise
16016             cls: 'tab-pane item',
16017             html: this.html || ''
16018         };
16019         
16020         if(this.active){
16021             cfg.cls += ' active';
16022         }
16023         
16024         if(this.tabId){
16025             cfg.tabId = this.tabId;
16026         }
16027         
16028         
16029         return cfg;
16030     },
16031     
16032     initEvents:  function()
16033     {
16034         Roo.log('-------- init events on tab panel ---------');
16035         
16036         var p = this.parent();
16037         this.navId = this.navId || p.navId;
16038         
16039         if (typeof(this.navId) != 'undefined') {
16040             // not really needed.. but just in case.. parent should be a NavGroup.
16041             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16042             Roo.log(['register', tg, this]);
16043             tg.register(this);
16044             
16045             var i = tg.tabs.length - 1;
16046             
16047             if(this.active && tg.bullets > 0 && i < tg.bullets){
16048                 tg.setActiveBullet(i);
16049             }
16050         }
16051         
16052     },
16053     
16054     
16055     onRender : function(ct, position)
16056     {
16057        // Roo.log("Call onRender: " + this.xtype);
16058         
16059         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16060         
16061         
16062         
16063         
16064         
16065     },
16066     
16067     setActive: function(state)
16068     {
16069         Roo.log("panel - set active " + this.tabId + "=" + state);
16070         
16071         this.active = state;
16072         if (!state) {
16073             this.el.removeClass('active');
16074             
16075         } else  if (!this.el.hasClass('active')) {
16076             this.el.addClass('active');
16077         }
16078         
16079         this.fireEvent('changed', this, state);
16080     }
16081     
16082     
16083 });
16084  
16085
16086  
16087
16088  /*
16089  * - LGPL
16090  *
16091  * DateField
16092  * 
16093  */
16094
16095 /**
16096  * @class Roo.bootstrap.DateField
16097  * @extends Roo.bootstrap.Input
16098  * Bootstrap DateField class
16099  * @cfg {Number} weekStart default 0
16100  * @cfg {String} viewMode default empty, (months|years)
16101  * @cfg {String} minViewMode default empty, (months|years)
16102  * @cfg {Number} startDate default -Infinity
16103  * @cfg {Number} endDate default Infinity
16104  * @cfg {Boolean} todayHighlight default false
16105  * @cfg {Boolean} todayBtn default false
16106  * @cfg {Boolean} calendarWeeks default false
16107  * @cfg {Object} daysOfWeekDisabled default empty
16108  * @cfg {Boolean} singleMode default false (true | false)
16109  * 
16110  * @cfg {Boolean} keyboardNavigation default true
16111  * @cfg {String} language default en
16112  * 
16113  * @constructor
16114  * Create a new DateField
16115  * @param {Object} config The config object
16116  */
16117
16118 Roo.bootstrap.DateField = function(config){
16119     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16120      this.addEvents({
16121             /**
16122              * @event show
16123              * Fires when this field show.
16124              * @param {Roo.bootstrap.DateField} this
16125              * @param {Mixed} date The date value
16126              */
16127             show : true,
16128             /**
16129              * @event show
16130              * Fires when this field hide.
16131              * @param {Roo.bootstrap.DateField} this
16132              * @param {Mixed} date The date value
16133              */
16134             hide : true,
16135             /**
16136              * @event select
16137              * Fires when select a date.
16138              * @param {Roo.bootstrap.DateField} this
16139              * @param {Mixed} date The date value
16140              */
16141             select : true
16142         });
16143 };
16144
16145 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16146     
16147     /**
16148      * @cfg {String} format
16149      * The default date format string which can be overriden for localization support.  The format must be
16150      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16151      */
16152     format : "m/d/y",
16153     /**
16154      * @cfg {String} altFormats
16155      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16156      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16157      */
16158     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16159     
16160     weekStart : 0,
16161     
16162     viewMode : '',
16163     
16164     minViewMode : '',
16165     
16166     todayHighlight : false,
16167     
16168     todayBtn: false,
16169     
16170     language: 'en',
16171     
16172     keyboardNavigation: true,
16173     
16174     calendarWeeks: false,
16175     
16176     startDate: -Infinity,
16177     
16178     endDate: Infinity,
16179     
16180     daysOfWeekDisabled: [],
16181     
16182     _events: [],
16183     
16184     singleMode : false,
16185     
16186     UTCDate: function()
16187     {
16188         return new Date(Date.UTC.apply(Date, arguments));
16189     },
16190     
16191     UTCToday: function()
16192     {
16193         var today = new Date();
16194         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16195     },
16196     
16197     getDate: function() {
16198             var d = this.getUTCDate();
16199             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16200     },
16201     
16202     getUTCDate: function() {
16203             return this.date;
16204     },
16205     
16206     setDate: function(d) {
16207             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16208     },
16209     
16210     setUTCDate: function(d) {
16211             this.date = d;
16212             this.setValue(this.formatDate(this.date));
16213     },
16214         
16215     onRender: function(ct, position)
16216     {
16217         
16218         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16219         
16220         this.language = this.language || 'en';
16221         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16222         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16223         
16224         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16225         this.format = this.format || 'm/d/y';
16226         this.isInline = false;
16227         this.isInput = true;
16228         this.component = this.el.select('.add-on', true).first() || false;
16229         this.component = (this.component && this.component.length === 0) ? false : this.component;
16230         this.hasInput = this.component && this.inputEL().length;
16231         
16232         if (typeof(this.minViewMode === 'string')) {
16233             switch (this.minViewMode) {
16234                 case 'months':
16235                     this.minViewMode = 1;
16236                     break;
16237                 case 'years':
16238                     this.minViewMode = 2;
16239                     break;
16240                 default:
16241                     this.minViewMode = 0;
16242                     break;
16243             }
16244         }
16245         
16246         if (typeof(this.viewMode === 'string')) {
16247             switch (this.viewMode) {
16248                 case 'months':
16249                     this.viewMode = 1;
16250                     break;
16251                 case 'years':
16252                     this.viewMode = 2;
16253                     break;
16254                 default:
16255                     this.viewMode = 0;
16256                     break;
16257             }
16258         }
16259                 
16260         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16261         
16262 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16263         
16264         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16265         
16266         this.picker().on('mousedown', this.onMousedown, this);
16267         this.picker().on('click', this.onClick, this);
16268         
16269         this.picker().addClass('datepicker-dropdown');
16270         
16271         this.startViewMode = this.viewMode;
16272         
16273         if(this.singleMode){
16274             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16275                 v.setVisibilityMode(Roo.Element.DISPLAY)
16276                 v.hide();
16277             });
16278             
16279             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16280                 v.setStyle('width', '189px');
16281             });
16282         }
16283         
16284         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16285             if(!this.calendarWeeks){
16286                 v.remove();
16287                 return;
16288             }
16289             
16290             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16291             v.attr('colspan', function(i, val){
16292                 return parseInt(val) + 1;
16293             });
16294         })
16295                         
16296         
16297         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16298         
16299         this.setStartDate(this.startDate);
16300         this.setEndDate(this.endDate);
16301         
16302         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16303         
16304         this.fillDow();
16305         this.fillMonths();
16306         this.update();
16307         this.showMode();
16308         
16309         if(this.isInline) {
16310             this.show();
16311         }
16312     },
16313     
16314     picker : function()
16315     {
16316         return this.pickerEl;
16317 //        return this.el.select('.datepicker', true).first();
16318     },
16319     
16320     fillDow: function()
16321     {
16322         var dowCnt = this.weekStart;
16323         
16324         var dow = {
16325             tag: 'tr',
16326             cn: [
16327                 
16328             ]
16329         };
16330         
16331         if(this.calendarWeeks){
16332             dow.cn.push({
16333                 tag: 'th',
16334                 cls: 'cw',
16335                 html: '&nbsp;'
16336             })
16337         }
16338         
16339         while (dowCnt < this.weekStart + 7) {
16340             dow.cn.push({
16341                 tag: 'th',
16342                 cls: 'dow',
16343                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16344             });
16345         }
16346         
16347         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16348     },
16349     
16350     fillMonths: function()
16351     {    
16352         var i = 0;
16353         var months = this.picker().select('>.datepicker-months td', true).first();
16354         
16355         months.dom.innerHTML = '';
16356         
16357         while (i < 12) {
16358             var month = {
16359                 tag: 'span',
16360                 cls: 'month',
16361                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16362             }
16363             
16364             months.createChild(month);
16365         }
16366         
16367     },
16368     
16369     update: function()
16370     {
16371         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;
16372         
16373         if (this.date < this.startDate) {
16374             this.viewDate = new Date(this.startDate);
16375         } else if (this.date > this.endDate) {
16376             this.viewDate = new Date(this.endDate);
16377         } else {
16378             this.viewDate = new Date(this.date);
16379         }
16380         
16381         this.fill();
16382     },
16383     
16384     fill: function() 
16385     {
16386         var d = new Date(this.viewDate),
16387                 year = d.getUTCFullYear(),
16388                 month = d.getUTCMonth(),
16389                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16390                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16391                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16392                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16393                 currentDate = this.date && this.date.valueOf(),
16394                 today = this.UTCToday();
16395         
16396         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16397         
16398 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16399         
16400 //        this.picker.select('>tfoot th.today').
16401 //                                              .text(dates[this.language].today)
16402 //                                              .toggle(this.todayBtn !== false);
16403     
16404         this.updateNavArrows();
16405         this.fillMonths();
16406                                                 
16407         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16408         
16409         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16410          
16411         prevMonth.setUTCDate(day);
16412         
16413         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16414         
16415         var nextMonth = new Date(prevMonth);
16416         
16417         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16418         
16419         nextMonth = nextMonth.valueOf();
16420         
16421         var fillMonths = false;
16422         
16423         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16424         
16425         while(prevMonth.valueOf() < nextMonth) {
16426             var clsName = '';
16427             
16428             if (prevMonth.getUTCDay() === this.weekStart) {
16429                 if(fillMonths){
16430                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16431                 }
16432                     
16433                 fillMonths = {
16434                     tag: 'tr',
16435                     cn: []
16436                 };
16437                 
16438                 if(this.calendarWeeks){
16439                     // ISO 8601: First week contains first thursday.
16440                     // ISO also states week starts on Monday, but we can be more abstract here.
16441                     var
16442                     // Start of current week: based on weekstart/current date
16443                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16444                     // Thursday of this week
16445                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16446                     // First Thursday of year, year from thursday
16447                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16448                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16449                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16450                     
16451                     fillMonths.cn.push({
16452                         tag: 'td',
16453                         cls: 'cw',
16454                         html: calWeek
16455                     });
16456                 }
16457             }
16458             
16459             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16460                 clsName += ' old';
16461             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16462                 clsName += ' new';
16463             }
16464             if (this.todayHighlight &&
16465                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16466                 prevMonth.getUTCMonth() == today.getMonth() &&
16467                 prevMonth.getUTCDate() == today.getDate()) {
16468                 clsName += ' today';
16469             }
16470             
16471             if (currentDate && prevMonth.valueOf() === currentDate) {
16472                 clsName += ' active';
16473             }
16474             
16475             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16476                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16477                     clsName += ' disabled';
16478             }
16479             
16480             fillMonths.cn.push({
16481                 tag: 'td',
16482                 cls: 'day ' + clsName,
16483                 html: prevMonth.getDate()
16484             })
16485             
16486             prevMonth.setDate(prevMonth.getDate()+1);
16487         }
16488           
16489         var currentYear = this.date && this.date.getUTCFullYear();
16490         var currentMonth = this.date && this.date.getUTCMonth();
16491         
16492         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16493         
16494         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16495             v.removeClass('active');
16496             
16497             if(currentYear === year && k === currentMonth){
16498                 v.addClass('active');
16499             }
16500             
16501             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16502                 v.addClass('disabled');
16503             }
16504             
16505         });
16506         
16507         
16508         year = parseInt(year/10, 10) * 10;
16509         
16510         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16511         
16512         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16513         
16514         year -= 1;
16515         for (var i = -1; i < 11; i++) {
16516             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16517                 tag: 'span',
16518                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16519                 html: year
16520             })
16521             
16522             year += 1;
16523         }
16524     },
16525     
16526     showMode: function(dir) 
16527     {
16528         if (dir) {
16529             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16530         }
16531         
16532         Roo.each(this.picker().select('>div',true).elements, function(v){
16533             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16534             v.hide();
16535         });
16536         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16537     },
16538     
16539     place: function()
16540     {
16541         if(this.isInline) return;
16542         
16543         this.picker().removeClass(['bottom', 'top']);
16544         
16545         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16546             /*
16547              * place to the top of element!
16548              *
16549              */
16550             
16551             this.picker().addClass('top');
16552             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16553             
16554             return;
16555         }
16556         
16557         this.picker().addClass('bottom');
16558         
16559         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16560     },
16561     
16562     parseDate : function(value)
16563     {
16564         if(!value || value instanceof Date){
16565             return value;
16566         }
16567         var v = Date.parseDate(value, this.format);
16568         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16569             v = Date.parseDate(value, 'Y-m-d');
16570         }
16571         if(!v && this.altFormats){
16572             if(!this.altFormatsArray){
16573                 this.altFormatsArray = this.altFormats.split("|");
16574             }
16575             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16576                 v = Date.parseDate(value, this.altFormatsArray[i]);
16577             }
16578         }
16579         return v;
16580     },
16581     
16582     formatDate : function(date, fmt)
16583     {   
16584         return (!date || !(date instanceof Date)) ?
16585         date : date.dateFormat(fmt || this.format);
16586     },
16587     
16588     onFocus : function()
16589     {
16590         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16591         this.show();
16592     },
16593     
16594     onBlur : function()
16595     {
16596         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16597         
16598         var d = this.inputEl().getValue();
16599         
16600         this.setValue(d);
16601                 
16602         this.hide();
16603     },
16604     
16605     show : function()
16606     {
16607         this.picker().show();
16608         this.update();
16609         this.place();
16610         
16611         this.fireEvent('show', this, this.date);
16612     },
16613     
16614     hide : function()
16615     {
16616         if(this.isInline) return;
16617         this.picker().hide();
16618         this.viewMode = this.startViewMode;
16619         this.showMode();
16620         
16621         this.fireEvent('hide', this, this.date);
16622         
16623     },
16624     
16625     onMousedown: function(e)
16626     {
16627         e.stopPropagation();
16628         e.preventDefault();
16629     },
16630     
16631     keyup: function(e)
16632     {
16633         Roo.bootstrap.DateField.superclass.keyup.call(this);
16634         this.update();
16635     },
16636
16637     setValue: function(v)
16638     {
16639         
16640         // v can be a string or a date..
16641         
16642         
16643         var d = new Date(this.parseDate(v) ).clearTime();
16644         
16645         if(isNaN(d.getTime())){
16646             this.date = this.viewDate = '';
16647             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16648             return;
16649         }
16650         
16651         v = this.formatDate(d);
16652         
16653         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16654         
16655         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16656      
16657         this.update();
16658
16659         this.fireEvent('select', this, this.date);
16660         
16661     },
16662     
16663     getValue: function()
16664     {
16665         return this.formatDate(this.date);
16666     },
16667     
16668     fireKey: function(e)
16669     {
16670         if (!this.picker().isVisible()){
16671             if (e.keyCode == 27) // allow escape to hide and re-show picker
16672                 this.show();
16673             return;
16674         }
16675         
16676         var dateChanged = false,
16677         dir, day, month,
16678         newDate, newViewDate;
16679         
16680         switch(e.keyCode){
16681             case 27: // escape
16682                 this.hide();
16683                 e.preventDefault();
16684                 break;
16685             case 37: // left
16686             case 39: // right
16687                 if (!this.keyboardNavigation) break;
16688                 dir = e.keyCode == 37 ? -1 : 1;
16689                 
16690                 if (e.ctrlKey){
16691                     newDate = this.moveYear(this.date, dir);
16692                     newViewDate = this.moveYear(this.viewDate, dir);
16693                 } else if (e.shiftKey){
16694                     newDate = this.moveMonth(this.date, dir);
16695                     newViewDate = this.moveMonth(this.viewDate, dir);
16696                 } else {
16697                     newDate = new Date(this.date);
16698                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16699                     newViewDate = new Date(this.viewDate);
16700                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16701                 }
16702                 if (this.dateWithinRange(newDate)){
16703                     this.date = newDate;
16704                     this.viewDate = newViewDate;
16705                     this.setValue(this.formatDate(this.date));
16706 //                    this.update();
16707                     e.preventDefault();
16708                     dateChanged = true;
16709                 }
16710                 break;
16711             case 38: // up
16712             case 40: // down
16713                 if (!this.keyboardNavigation) break;
16714                 dir = e.keyCode == 38 ? -1 : 1;
16715                 if (e.ctrlKey){
16716                     newDate = this.moveYear(this.date, dir);
16717                     newViewDate = this.moveYear(this.viewDate, dir);
16718                 } else if (e.shiftKey){
16719                     newDate = this.moveMonth(this.date, dir);
16720                     newViewDate = this.moveMonth(this.viewDate, dir);
16721                 } else {
16722                     newDate = new Date(this.date);
16723                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16724                     newViewDate = new Date(this.viewDate);
16725                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16726                 }
16727                 if (this.dateWithinRange(newDate)){
16728                     this.date = newDate;
16729                     this.viewDate = newViewDate;
16730                     this.setValue(this.formatDate(this.date));
16731 //                    this.update();
16732                     e.preventDefault();
16733                     dateChanged = true;
16734                 }
16735                 break;
16736             case 13: // enter
16737                 this.setValue(this.formatDate(this.date));
16738                 this.hide();
16739                 e.preventDefault();
16740                 break;
16741             case 9: // tab
16742                 this.setValue(this.formatDate(this.date));
16743                 this.hide();
16744                 break;
16745             case 16: // shift
16746             case 17: // ctrl
16747             case 18: // alt
16748                 break;
16749             default :
16750                 this.hide();
16751                 
16752         }
16753     },
16754     
16755     
16756     onClick: function(e) 
16757     {
16758         e.stopPropagation();
16759         e.preventDefault();
16760         
16761         var target = e.getTarget();
16762         
16763         if(target.nodeName.toLowerCase() === 'i'){
16764             target = Roo.get(target).dom.parentNode;
16765         }
16766         
16767         var nodeName = target.nodeName;
16768         var className = target.className;
16769         var html = target.innerHTML;
16770         //Roo.log(nodeName);
16771         
16772         switch(nodeName.toLowerCase()) {
16773             case 'th':
16774                 switch(className) {
16775                     case 'switch':
16776                         this.showMode(1);
16777                         break;
16778                     case 'prev':
16779                     case 'next':
16780                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16781                         switch(this.viewMode){
16782                                 case 0:
16783                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16784                                         break;
16785                                 case 1:
16786                                 case 2:
16787                                         this.viewDate = this.moveYear(this.viewDate, dir);
16788                                         break;
16789                         }
16790                         this.fill();
16791                         break;
16792                     case 'today':
16793                         var date = new Date();
16794                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16795 //                        this.fill()
16796                         this.setValue(this.formatDate(this.date));
16797                         
16798                         this.hide();
16799                         break;
16800                 }
16801                 break;
16802             case 'span':
16803                 if (className.indexOf('disabled') < 0) {
16804                     this.viewDate.setUTCDate(1);
16805                     if (className.indexOf('month') > -1) {
16806                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16807                     } else {
16808                         var year = parseInt(html, 10) || 0;
16809                         this.viewDate.setUTCFullYear(year);
16810                         
16811                     }
16812                     
16813                     if(this.singleMode){
16814                         this.setValue(this.formatDate(this.viewDate));
16815                         this.hide();
16816                         return;
16817                     }
16818                     
16819                     this.showMode(-1);
16820                     this.fill();
16821                 }
16822                 break;
16823                 
16824             case 'td':
16825                 //Roo.log(className);
16826                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16827                     var day = parseInt(html, 10) || 1;
16828                     var year = this.viewDate.getUTCFullYear(),
16829                         month = this.viewDate.getUTCMonth();
16830
16831                     if (className.indexOf('old') > -1) {
16832                         if(month === 0 ){
16833                             month = 11;
16834                             year -= 1;
16835                         }else{
16836                             month -= 1;
16837                         }
16838                     } else if (className.indexOf('new') > -1) {
16839                         if (month == 11) {
16840                             month = 0;
16841                             year += 1;
16842                         } else {
16843                             month += 1;
16844                         }
16845                     }
16846                     //Roo.log([year,month,day]);
16847                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16848                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16849 //                    this.fill();
16850                     //Roo.log(this.formatDate(this.date));
16851                     this.setValue(this.formatDate(this.date));
16852                     this.hide();
16853                 }
16854                 break;
16855         }
16856     },
16857     
16858     setStartDate: function(startDate)
16859     {
16860         this.startDate = startDate || -Infinity;
16861         if (this.startDate !== -Infinity) {
16862             this.startDate = this.parseDate(this.startDate);
16863         }
16864         this.update();
16865         this.updateNavArrows();
16866     },
16867
16868     setEndDate: function(endDate)
16869     {
16870         this.endDate = endDate || Infinity;
16871         if (this.endDate !== Infinity) {
16872             this.endDate = this.parseDate(this.endDate);
16873         }
16874         this.update();
16875         this.updateNavArrows();
16876     },
16877     
16878     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16879     {
16880         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16881         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16882             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16883         }
16884         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16885             return parseInt(d, 10);
16886         });
16887         this.update();
16888         this.updateNavArrows();
16889     },
16890     
16891     updateNavArrows: function() 
16892     {
16893         if(this.singleMode){
16894             return;
16895         }
16896         
16897         var d = new Date(this.viewDate),
16898         year = d.getUTCFullYear(),
16899         month = d.getUTCMonth();
16900         
16901         Roo.each(this.picker().select('.prev', true).elements, function(v){
16902             v.show();
16903             switch (this.viewMode) {
16904                 case 0:
16905
16906                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16907                         v.hide();
16908                     }
16909                     break;
16910                 case 1:
16911                 case 2:
16912                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16913                         v.hide();
16914                     }
16915                     break;
16916             }
16917         });
16918         
16919         Roo.each(this.picker().select('.next', true).elements, function(v){
16920             v.show();
16921             switch (this.viewMode) {
16922                 case 0:
16923
16924                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16925                         v.hide();
16926                     }
16927                     break;
16928                 case 1:
16929                 case 2:
16930                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16931                         v.hide();
16932                     }
16933                     break;
16934             }
16935         })
16936     },
16937     
16938     moveMonth: function(date, dir)
16939     {
16940         if (!dir) return date;
16941         var new_date = new Date(date.valueOf()),
16942         day = new_date.getUTCDate(),
16943         month = new_date.getUTCMonth(),
16944         mag = Math.abs(dir),
16945         new_month, test;
16946         dir = dir > 0 ? 1 : -1;
16947         if (mag == 1){
16948             test = dir == -1
16949             // If going back one month, make sure month is not current month
16950             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16951             ? function(){
16952                 return new_date.getUTCMonth() == month;
16953             }
16954             // If going forward one month, make sure month is as expected
16955             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16956             : function(){
16957                 return new_date.getUTCMonth() != new_month;
16958             };
16959             new_month = month + dir;
16960             new_date.setUTCMonth(new_month);
16961             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16962             if (new_month < 0 || new_month > 11)
16963                 new_month = (new_month + 12) % 12;
16964         } else {
16965             // For magnitudes >1, move one month at a time...
16966             for (var i=0; i<mag; i++)
16967                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16968                 new_date = this.moveMonth(new_date, dir);
16969             // ...then reset the day, keeping it in the new month
16970             new_month = new_date.getUTCMonth();
16971             new_date.setUTCDate(day);
16972             test = function(){
16973                 return new_month != new_date.getUTCMonth();
16974             };
16975         }
16976         // Common date-resetting loop -- if date is beyond end of month, make it
16977         // end of month
16978         while (test()){
16979             new_date.setUTCDate(--day);
16980             new_date.setUTCMonth(new_month);
16981         }
16982         return new_date;
16983     },
16984
16985     moveYear: function(date, dir)
16986     {
16987         return this.moveMonth(date, dir*12);
16988     },
16989
16990     dateWithinRange: function(date)
16991     {
16992         return date >= this.startDate && date <= this.endDate;
16993     },
16994
16995     
16996     remove: function() 
16997     {
16998         this.picker().remove();
16999     }
17000    
17001 });
17002
17003 Roo.apply(Roo.bootstrap.DateField,  {
17004     
17005     head : {
17006         tag: 'thead',
17007         cn: [
17008         {
17009             tag: 'tr',
17010             cn: [
17011             {
17012                 tag: 'th',
17013                 cls: 'prev',
17014                 html: '<i class="fa fa-arrow-left"/>'
17015             },
17016             {
17017                 tag: 'th',
17018                 cls: 'switch',
17019                 colspan: '5'
17020             },
17021             {
17022                 tag: 'th',
17023                 cls: 'next',
17024                 html: '<i class="fa fa-arrow-right"/>'
17025             }
17026
17027             ]
17028         }
17029         ]
17030     },
17031     
17032     content : {
17033         tag: 'tbody',
17034         cn: [
17035         {
17036             tag: 'tr',
17037             cn: [
17038             {
17039                 tag: 'td',
17040                 colspan: '7'
17041             }
17042             ]
17043         }
17044         ]
17045     },
17046     
17047     footer : {
17048         tag: 'tfoot',
17049         cn: [
17050         {
17051             tag: 'tr',
17052             cn: [
17053             {
17054                 tag: 'th',
17055                 colspan: '7',
17056                 cls: 'today'
17057             }
17058                     
17059             ]
17060         }
17061         ]
17062     },
17063     
17064     dates:{
17065         en: {
17066             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17067             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17068             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17069             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17070             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17071             today: "Today"
17072         }
17073     },
17074     
17075     modes: [
17076     {
17077         clsName: 'days',
17078         navFnc: 'Month',
17079         navStep: 1
17080     },
17081     {
17082         clsName: 'months',
17083         navFnc: 'FullYear',
17084         navStep: 1
17085     },
17086     {
17087         clsName: 'years',
17088         navFnc: 'FullYear',
17089         navStep: 10
17090     }]
17091 });
17092
17093 Roo.apply(Roo.bootstrap.DateField,  {
17094   
17095     template : {
17096         tag: 'div',
17097         cls: 'datepicker dropdown-menu roo-dynamic',
17098         cn: [
17099         {
17100             tag: 'div',
17101             cls: 'datepicker-days',
17102             cn: [
17103             {
17104                 tag: 'table',
17105                 cls: 'table-condensed',
17106                 cn:[
17107                 Roo.bootstrap.DateField.head,
17108                 {
17109                     tag: 'tbody'
17110                 },
17111                 Roo.bootstrap.DateField.footer
17112                 ]
17113             }
17114             ]
17115         },
17116         {
17117             tag: 'div',
17118             cls: 'datepicker-months',
17119             cn: [
17120             {
17121                 tag: 'table',
17122                 cls: 'table-condensed',
17123                 cn:[
17124                 Roo.bootstrap.DateField.head,
17125                 Roo.bootstrap.DateField.content,
17126                 Roo.bootstrap.DateField.footer
17127                 ]
17128             }
17129             ]
17130         },
17131         {
17132             tag: 'div',
17133             cls: 'datepicker-years',
17134             cn: [
17135             {
17136                 tag: 'table',
17137                 cls: 'table-condensed',
17138                 cn:[
17139                 Roo.bootstrap.DateField.head,
17140                 Roo.bootstrap.DateField.content,
17141                 Roo.bootstrap.DateField.footer
17142                 ]
17143             }
17144             ]
17145         }
17146         ]
17147     }
17148 });
17149
17150  
17151
17152  /*
17153  * - LGPL
17154  *
17155  * TimeField
17156  * 
17157  */
17158
17159 /**
17160  * @class Roo.bootstrap.TimeField
17161  * @extends Roo.bootstrap.Input
17162  * Bootstrap DateField class
17163  * 
17164  * 
17165  * @constructor
17166  * Create a new TimeField
17167  * @param {Object} config The config object
17168  */
17169
17170 Roo.bootstrap.TimeField = function(config){
17171     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17172     this.addEvents({
17173             /**
17174              * @event show
17175              * Fires when this field show.
17176              * @param {Roo.bootstrap.DateField} thisthis
17177              * @param {Mixed} date The date value
17178              */
17179             show : true,
17180             /**
17181              * @event show
17182              * Fires when this field hide.
17183              * @param {Roo.bootstrap.DateField} this
17184              * @param {Mixed} date The date value
17185              */
17186             hide : true,
17187             /**
17188              * @event select
17189              * Fires when select a date.
17190              * @param {Roo.bootstrap.DateField} this
17191              * @param {Mixed} date The date value
17192              */
17193             select : true
17194         });
17195 };
17196
17197 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17198     
17199     /**
17200      * @cfg {String} format
17201      * The default time format string which can be overriden for localization support.  The format must be
17202      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17203      */
17204     format : "H:i",
17205        
17206     onRender: function(ct, position)
17207     {
17208         
17209         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17210                 
17211         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17212         
17213         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17214         
17215         this.pop = this.picker().select('>.datepicker-time',true).first();
17216         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17217         
17218         this.picker().on('mousedown', this.onMousedown, this);
17219         this.picker().on('click', this.onClick, this);
17220         
17221         this.picker().addClass('datepicker-dropdown');
17222     
17223         this.fillTime();
17224         this.update();
17225             
17226         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17227         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17228         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17229         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17230         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17231         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17232
17233     },
17234     
17235     fireKey: function(e){
17236         if (!this.picker().isVisible()){
17237             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17238                 this.show();
17239             }
17240             return;
17241         }
17242
17243         e.preventDefault();
17244         
17245         switch(e.keyCode){
17246             case 27: // escape
17247                 this.hide();
17248                 break;
17249             case 37: // left
17250             case 39: // right
17251                 this.onTogglePeriod();
17252                 break;
17253             case 38: // up
17254                 this.onIncrementMinutes();
17255                 break;
17256             case 40: // down
17257                 this.onDecrementMinutes();
17258                 break;
17259             case 13: // enter
17260             case 9: // tab
17261                 this.setTime();
17262                 break;
17263         }
17264     },
17265     
17266     onClick: function(e) {
17267         e.stopPropagation();
17268         e.preventDefault();
17269     },
17270     
17271     picker : function()
17272     {
17273         return this.el.select('.datepicker', true).first();
17274     },
17275     
17276     fillTime: function()
17277     {    
17278         var time = this.pop.select('tbody', true).first();
17279         
17280         time.dom.innerHTML = '';
17281         
17282         time.createChild({
17283             tag: 'tr',
17284             cn: [
17285                 {
17286                     tag: 'td',
17287                     cn: [
17288                         {
17289                             tag: 'a',
17290                             href: '#',
17291                             cls: 'btn',
17292                             cn: [
17293                                 {
17294                                     tag: 'span',
17295                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17296                                 }
17297                             ]
17298                         } 
17299                     ]
17300                 },
17301                 {
17302                     tag: 'td',
17303                     cls: 'separator'
17304                 },
17305                 {
17306                     tag: 'td',
17307                     cn: [
17308                         {
17309                             tag: 'a',
17310                             href: '#',
17311                             cls: 'btn',
17312                             cn: [
17313                                 {
17314                                     tag: 'span',
17315                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17316                                 }
17317                             ]
17318                         }
17319                     ]
17320                 },
17321                 {
17322                     tag: 'td',
17323                     cls: 'separator'
17324                 }
17325             ]
17326         });
17327         
17328         time.createChild({
17329             tag: 'tr',
17330             cn: [
17331                 {
17332                     tag: 'td',
17333                     cn: [
17334                         {
17335                             tag: 'span',
17336                             cls: 'timepicker-hour',
17337                             html: '00'
17338                         }  
17339                     ]
17340                 },
17341                 {
17342                     tag: 'td',
17343                     cls: 'separator',
17344                     html: ':'
17345                 },
17346                 {
17347                     tag: 'td',
17348                     cn: [
17349                         {
17350                             tag: 'span',
17351                             cls: 'timepicker-minute',
17352                             html: '00'
17353                         }  
17354                     ]
17355                 },
17356                 {
17357                     tag: 'td',
17358                     cls: 'separator'
17359                 },
17360                 {
17361                     tag: 'td',
17362                     cn: [
17363                         {
17364                             tag: 'button',
17365                             type: 'button',
17366                             cls: 'btn btn-primary period',
17367                             html: 'AM'
17368                             
17369                         }
17370                     ]
17371                 }
17372             ]
17373         });
17374         
17375         time.createChild({
17376             tag: 'tr',
17377             cn: [
17378                 {
17379                     tag: 'td',
17380                     cn: [
17381                         {
17382                             tag: 'a',
17383                             href: '#',
17384                             cls: 'btn',
17385                             cn: [
17386                                 {
17387                                     tag: 'span',
17388                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17389                                 }
17390                             ]
17391                         }
17392                     ]
17393                 },
17394                 {
17395                     tag: 'td',
17396                     cls: 'separator'
17397                 },
17398                 {
17399                     tag: 'td',
17400                     cn: [
17401                         {
17402                             tag: 'a',
17403                             href: '#',
17404                             cls: 'btn',
17405                             cn: [
17406                                 {
17407                                     tag: 'span',
17408                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17409                                 }
17410                             ]
17411                         }
17412                     ]
17413                 },
17414                 {
17415                     tag: 'td',
17416                     cls: 'separator'
17417                 }
17418             ]
17419         });
17420         
17421     },
17422     
17423     update: function()
17424     {
17425         
17426         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17427         
17428         this.fill();
17429     },
17430     
17431     fill: function() 
17432     {
17433         var hours = this.time.getHours();
17434         var minutes = this.time.getMinutes();
17435         var period = 'AM';
17436         
17437         if(hours > 11){
17438             period = 'PM';
17439         }
17440         
17441         if(hours == 0){
17442             hours = 12;
17443         }
17444         
17445         
17446         if(hours > 12){
17447             hours = hours - 12;
17448         }
17449         
17450         if(hours < 10){
17451             hours = '0' + hours;
17452         }
17453         
17454         if(minutes < 10){
17455             minutes = '0' + minutes;
17456         }
17457         
17458         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17459         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17460         this.pop.select('button', true).first().dom.innerHTML = period;
17461         
17462     },
17463     
17464     place: function()
17465     {   
17466         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17467         
17468         var cls = ['bottom'];
17469         
17470         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17471             cls.pop();
17472             cls.push('top');
17473         }
17474         
17475         cls.push('right');
17476         
17477         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17478             cls.pop();
17479             cls.push('left');
17480         }
17481         
17482         this.picker().addClass(cls.join('-'));
17483         
17484         var _this = this;
17485         
17486         Roo.each(cls, function(c){
17487             if(c == 'bottom'){
17488                 _this.picker().setTop(_this.inputEl().getHeight());
17489                 return;
17490             }
17491             if(c == 'top'){
17492                 _this.picker().setTop(0 - _this.picker().getHeight());
17493                 return;
17494             }
17495             
17496             if(c == 'left'){
17497                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17498                 return;
17499             }
17500             if(c == 'right'){
17501                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17502                 return;
17503             }
17504         });
17505         
17506     },
17507   
17508     onFocus : function()
17509     {
17510         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17511         this.show();
17512     },
17513     
17514     onBlur : function()
17515     {
17516         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17517         this.hide();
17518     },
17519     
17520     show : function()
17521     {
17522         this.picker().show();
17523         this.pop.show();
17524         this.update();
17525         this.place();
17526         
17527         this.fireEvent('show', this, this.date);
17528     },
17529     
17530     hide : function()
17531     {
17532         this.picker().hide();
17533         this.pop.hide();
17534         
17535         this.fireEvent('hide', this, this.date);
17536     },
17537     
17538     setTime : function()
17539     {
17540         this.hide();
17541         this.setValue(this.time.format(this.format));
17542         
17543         this.fireEvent('select', this, this.date);
17544         
17545         
17546     },
17547     
17548     onMousedown: function(e){
17549         e.stopPropagation();
17550         e.preventDefault();
17551     },
17552     
17553     onIncrementHours: function()
17554     {
17555         Roo.log('onIncrementHours');
17556         this.time = this.time.add(Date.HOUR, 1);
17557         this.update();
17558         
17559     },
17560     
17561     onDecrementHours: function()
17562     {
17563         Roo.log('onDecrementHours');
17564         this.time = this.time.add(Date.HOUR, -1);
17565         this.update();
17566     },
17567     
17568     onIncrementMinutes: function()
17569     {
17570         Roo.log('onIncrementMinutes');
17571         this.time = this.time.add(Date.MINUTE, 1);
17572         this.update();
17573     },
17574     
17575     onDecrementMinutes: function()
17576     {
17577         Roo.log('onDecrementMinutes');
17578         this.time = this.time.add(Date.MINUTE, -1);
17579         this.update();
17580     },
17581     
17582     onTogglePeriod: function()
17583     {
17584         Roo.log('onTogglePeriod');
17585         this.time = this.time.add(Date.HOUR, 12);
17586         this.update();
17587     }
17588     
17589    
17590 });
17591
17592 Roo.apply(Roo.bootstrap.TimeField,  {
17593     
17594     content : {
17595         tag: 'tbody',
17596         cn: [
17597             {
17598                 tag: 'tr',
17599                 cn: [
17600                 {
17601                     tag: 'td',
17602                     colspan: '7'
17603                 }
17604                 ]
17605             }
17606         ]
17607     },
17608     
17609     footer : {
17610         tag: 'tfoot',
17611         cn: [
17612             {
17613                 tag: 'tr',
17614                 cn: [
17615                 {
17616                     tag: 'th',
17617                     colspan: '7',
17618                     cls: '',
17619                     cn: [
17620                         {
17621                             tag: 'button',
17622                             cls: 'btn btn-info ok',
17623                             html: 'OK'
17624                         }
17625                     ]
17626                 }
17627
17628                 ]
17629             }
17630         ]
17631     }
17632 });
17633
17634 Roo.apply(Roo.bootstrap.TimeField,  {
17635   
17636     template : {
17637         tag: 'div',
17638         cls: 'datepicker dropdown-menu',
17639         cn: [
17640             {
17641                 tag: 'div',
17642                 cls: 'datepicker-time',
17643                 cn: [
17644                 {
17645                     tag: 'table',
17646                     cls: 'table-condensed',
17647                     cn:[
17648                     Roo.bootstrap.TimeField.content,
17649                     Roo.bootstrap.TimeField.footer
17650                     ]
17651                 }
17652                 ]
17653             }
17654         ]
17655     }
17656 });
17657
17658  
17659
17660  /*
17661  * - LGPL
17662  *
17663  * MonthField
17664  * 
17665  */
17666
17667 /**
17668  * @class Roo.bootstrap.MonthField
17669  * @extends Roo.bootstrap.Input
17670  * Bootstrap MonthField class
17671  * 
17672  * @cfg {String} language default en
17673  * 
17674  * @constructor
17675  * Create a new MonthField
17676  * @param {Object} config The config object
17677  */
17678
17679 Roo.bootstrap.MonthField = function(config){
17680     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17681     
17682     this.addEvents({
17683         /**
17684          * @event show
17685          * Fires when this field show.
17686          * @param {Roo.bootstrap.MonthField} this
17687          * @param {Mixed} date The date value
17688          */
17689         show : true,
17690         /**
17691          * @event show
17692          * Fires when this field hide.
17693          * @param {Roo.bootstrap.MonthField} this
17694          * @param {Mixed} date The date value
17695          */
17696         hide : true,
17697         /**
17698          * @event select
17699          * Fires when select a date.
17700          * @param {Roo.bootstrap.MonthField} this
17701          * @param {String} oldvalue The old value
17702          * @param {String} newvalue The new value
17703          */
17704         select : true
17705     });
17706 };
17707
17708 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17709     
17710     onRender: function(ct, position)
17711     {
17712         
17713         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17714         
17715         this.language = this.language || 'en';
17716         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17717         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17718         
17719         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17720         this.isInline = false;
17721         this.isInput = true;
17722         this.component = this.el.select('.add-on', true).first() || false;
17723         this.component = (this.component && this.component.length === 0) ? false : this.component;
17724         this.hasInput = this.component && this.inputEL().length;
17725         
17726         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17727         
17728         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17729         
17730         this.picker().on('mousedown', this.onMousedown, this);
17731         this.picker().on('click', this.onClick, this);
17732         
17733         this.picker().addClass('datepicker-dropdown');
17734         
17735         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17736             v.setStyle('width', '189px');
17737         });
17738         
17739         this.fillMonths();
17740         
17741         this.update();
17742         
17743         if(this.isInline) {
17744             this.show();
17745         }
17746         
17747     },
17748     
17749     setValue: function(v, suppressEvent)
17750     {   
17751         var o = this.getValue();
17752         
17753         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17754         
17755         this.update();
17756
17757         if(suppressEvent !== true){
17758             this.fireEvent('select', this, o, v);
17759         }
17760         
17761     },
17762     
17763     getValue: function()
17764     {
17765         return this.value;
17766     },
17767     
17768     onClick: function(e) 
17769     {
17770         e.stopPropagation();
17771         e.preventDefault();
17772         
17773         var target = e.getTarget();
17774         
17775         if(target.nodeName.toLowerCase() === 'i'){
17776             target = Roo.get(target).dom.parentNode;
17777         }
17778         
17779         var nodeName = target.nodeName;
17780         var className = target.className;
17781         var html = target.innerHTML;
17782         
17783         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17784             return;
17785         }
17786         
17787         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17788         
17789         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17790         
17791         this.hide();
17792                         
17793     },
17794     
17795     picker : function()
17796     {
17797         return this.pickerEl;
17798     },
17799     
17800     fillMonths: function()
17801     {    
17802         var i = 0;
17803         var months = this.picker().select('>.datepicker-months td', true).first();
17804         
17805         months.dom.innerHTML = '';
17806         
17807         while (i < 12) {
17808             var month = {
17809                 tag: 'span',
17810                 cls: 'month',
17811                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17812             }
17813             
17814             months.createChild(month);
17815         }
17816         
17817     },
17818     
17819     update: function()
17820     {
17821         var _this = this;
17822         
17823         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17824             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17825         }
17826         
17827         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17828             e.removeClass('active');
17829             
17830             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17831                 e.addClass('active');
17832             }
17833         })
17834     },
17835     
17836     place: function()
17837     {
17838         if(this.isInline) return;
17839         
17840         this.picker().removeClass(['bottom', 'top']);
17841         
17842         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17843             /*
17844              * place to the top of element!
17845              *
17846              */
17847             
17848             this.picker().addClass('top');
17849             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17850             
17851             return;
17852         }
17853         
17854         this.picker().addClass('bottom');
17855         
17856         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17857     },
17858     
17859     onFocus : function()
17860     {
17861         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17862         this.show();
17863     },
17864     
17865     onBlur : function()
17866     {
17867         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17868         
17869         var d = this.inputEl().getValue();
17870         
17871         this.setValue(d);
17872                 
17873         this.hide();
17874     },
17875     
17876     show : function()
17877     {
17878         this.picker().show();
17879         this.picker().select('>.datepicker-months', true).first().show();
17880         this.update();
17881         this.place();
17882         
17883         this.fireEvent('show', this, this.date);
17884     },
17885     
17886     hide : function()
17887     {
17888         if(this.isInline) return;
17889         this.picker().hide();
17890         this.fireEvent('hide', this, this.date);
17891         
17892     },
17893     
17894     onMousedown: function(e)
17895     {
17896         e.stopPropagation();
17897         e.preventDefault();
17898     },
17899     
17900     keyup: function(e)
17901     {
17902         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17903         this.update();
17904     },
17905
17906     fireKey: function(e)
17907     {
17908         if (!this.picker().isVisible()){
17909             if (e.keyCode == 27) // allow escape to hide and re-show picker
17910                 this.show();
17911             return;
17912         }
17913         
17914         var dir;
17915         
17916         switch(e.keyCode){
17917             case 27: // escape
17918                 this.hide();
17919                 e.preventDefault();
17920                 break;
17921             case 37: // left
17922             case 39: // right
17923                 dir = e.keyCode == 37 ? -1 : 1;
17924                 
17925                 this.vIndex = this.vIndex + dir;
17926                 
17927                 if(this.vIndex < 0){
17928                     this.vIndex = 0;
17929                 }
17930                 
17931                 if(this.vIndex > 11){
17932                     this.vIndex = 11;
17933                 }
17934                 
17935                 if(isNaN(this.vIndex)){
17936                     this.vIndex = 0;
17937                 }
17938                 
17939                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17940                 
17941                 break;
17942             case 38: // up
17943             case 40: // down
17944                 
17945                 dir = e.keyCode == 38 ? -1 : 1;
17946                 
17947                 this.vIndex = this.vIndex + dir * 4;
17948                 
17949                 if(this.vIndex < 0){
17950                     this.vIndex = 0;
17951                 }
17952                 
17953                 if(this.vIndex > 11){
17954                     this.vIndex = 11;
17955                 }
17956                 
17957                 if(isNaN(this.vIndex)){
17958                     this.vIndex = 0;
17959                 }
17960                 
17961                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17962                 break;
17963                 
17964             case 13: // enter
17965                 
17966                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17967                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17968                 }
17969                 
17970                 this.hide();
17971                 e.preventDefault();
17972                 break;
17973             case 9: // tab
17974                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17975                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17976                 }
17977                 this.hide();
17978                 break;
17979             case 16: // shift
17980             case 17: // ctrl
17981             case 18: // alt
17982                 break;
17983             default :
17984                 this.hide();
17985                 
17986         }
17987     },
17988     
17989     remove: function() 
17990     {
17991         this.picker().remove();
17992     }
17993    
17994 });
17995
17996 Roo.apply(Roo.bootstrap.MonthField,  {
17997     
17998     content : {
17999         tag: 'tbody',
18000         cn: [
18001         {
18002             tag: 'tr',
18003             cn: [
18004             {
18005                 tag: 'td',
18006                 colspan: '7'
18007             }
18008             ]
18009         }
18010         ]
18011     },
18012     
18013     dates:{
18014         en: {
18015             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18016             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18017         }
18018     }
18019 });
18020
18021 Roo.apply(Roo.bootstrap.MonthField,  {
18022   
18023     template : {
18024         tag: 'div',
18025         cls: 'datepicker dropdown-menu roo-dynamic',
18026         cn: [
18027             {
18028                 tag: 'div',
18029                 cls: 'datepicker-months',
18030                 cn: [
18031                 {
18032                     tag: 'table',
18033                     cls: 'table-condensed',
18034                     cn:[
18035                         Roo.bootstrap.DateField.content
18036                     ]
18037                 }
18038                 ]
18039             }
18040         ]
18041     }
18042 });
18043
18044  
18045
18046  
18047  /*
18048  * - LGPL
18049  *
18050  * CheckBox
18051  * 
18052  */
18053
18054 /**
18055  * @class Roo.bootstrap.CheckBox
18056  * @extends Roo.bootstrap.Input
18057  * Bootstrap CheckBox class
18058  * 
18059  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18060  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18061  * @cfg {String} boxLabel The text that appears beside the checkbox
18062  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18063  * @cfg {Boolean} checked initnal the element
18064  * @cfg {Boolean} inline inline the element (default false)
18065  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18066  * 
18067  * @constructor
18068  * Create a new CheckBox
18069  * @param {Object} config The config object
18070  */
18071
18072 Roo.bootstrap.CheckBox = function(config){
18073     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18074    
18075     this.addEvents({
18076         /**
18077         * @event check
18078         * Fires when the element is checked or unchecked.
18079         * @param {Roo.bootstrap.CheckBox} this This input
18080         * @param {Boolean} checked The new checked value
18081         */
18082        check : true
18083     });
18084     
18085 };
18086
18087 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18088   
18089     inputType: 'checkbox',
18090     inputValue: 1,
18091     valueOff: 0,
18092     boxLabel: false,
18093     checked: false,
18094     weight : false,
18095     inline: false,
18096     
18097     getAutoCreate : function()
18098     {
18099         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18100         
18101         var id = Roo.id();
18102         
18103         var cfg = {};
18104         
18105         cfg.cls = 'form-group ' + this.inputType; //input-group
18106         
18107         if(this.inline){
18108             cfg.cls += ' ' + this.inputType + '-inline';
18109         }
18110         
18111         var input =  {
18112             tag: 'input',
18113             id : id,
18114             type : this.inputType,
18115             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18116             cls : 'roo-' + this.inputType, //'form-box',
18117             placeholder : this.placeholder || ''
18118             
18119         };
18120         
18121         if (this.weight) { // Validity check?
18122             cfg.cls += " " + this.inputType + "-" + this.weight;
18123         }
18124         
18125         if (this.disabled) {
18126             input.disabled=true;
18127         }
18128         
18129         if(this.checked){
18130             input.checked = this.checked;
18131         }
18132         
18133         if (this.name) {
18134             input.name = this.name;
18135         }
18136         
18137         if (this.size) {
18138             input.cls += ' input-' + this.size;
18139         }
18140         
18141         var settings=this;
18142         
18143         ['xs','sm','md','lg'].map(function(size){
18144             if (settings[size]) {
18145                 cfg.cls += ' col-' + size + '-' + settings[size];
18146             }
18147         });
18148         
18149         var inputblock = input;
18150          
18151         if (this.before || this.after) {
18152             
18153             inputblock = {
18154                 cls : 'input-group',
18155                 cn :  [] 
18156             };
18157             
18158             if (this.before) {
18159                 inputblock.cn.push({
18160                     tag :'span',
18161                     cls : 'input-group-addon',
18162                     html : this.before
18163                 });
18164             }
18165             
18166             inputblock.cn.push(input);
18167             
18168             if (this.after) {
18169                 inputblock.cn.push({
18170                     tag :'span',
18171                     cls : 'input-group-addon',
18172                     html : this.after
18173                 });
18174             }
18175             
18176         }
18177         
18178         if (align ==='left' && this.fieldLabel.length) {
18179                 Roo.log("left and has label");
18180                 cfg.cn = [
18181                     
18182                     {
18183                         tag: 'label',
18184                         'for' :  id,
18185                         cls : 'control-label col-md-' + this.labelWidth,
18186                         html : this.fieldLabel
18187                         
18188                     },
18189                     {
18190                         cls : "col-md-" + (12 - this.labelWidth), 
18191                         cn: [
18192                             inputblock
18193                         ]
18194                     }
18195                     
18196                 ];
18197         } else if ( this.fieldLabel.length) {
18198                 Roo.log(" label");
18199                 cfg.cn = [
18200                    
18201                     {
18202                         tag: this.boxLabel ? 'span' : 'label',
18203                         'for': id,
18204                         cls: 'control-label box-input-label',
18205                         //cls : 'input-group-addon',
18206                         html : this.fieldLabel
18207                         
18208                     },
18209                     
18210                     inputblock
18211                     
18212                 ];
18213
18214         } else {
18215             
18216                 Roo.log(" no label && no align");
18217                 cfg.cn = [  inputblock ] ;
18218                 
18219                 
18220         }
18221         if(this.boxLabel){
18222              var boxLabelCfg = {
18223                 tag: 'label',
18224                 //'for': id, // box label is handled by onclick - so no for...
18225                 cls: 'box-label',
18226                 html: this.boxLabel
18227             }
18228             
18229             if(this.tooltip){
18230                 boxLabelCfg.tooltip = this.tooltip;
18231             }
18232              
18233             cfg.cn.push(boxLabelCfg);
18234         }
18235         
18236         
18237        
18238         return cfg;
18239         
18240     },
18241     
18242     /**
18243      * return the real input element.
18244      */
18245     inputEl: function ()
18246     {
18247         return this.el.select('input.roo-' + this.inputType,true).first();
18248     },
18249     
18250     labelEl: function()
18251     {
18252         return this.el.select('label.control-label',true).first();
18253     },
18254     /* depricated... */
18255     
18256     label: function()
18257     {
18258         return this.labelEl();
18259     },
18260     
18261     initEvents : function()
18262     {
18263 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18264         
18265         this.inputEl().on('click', this.onClick,  this);
18266         
18267         if (this.boxLabel) { 
18268             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18269         }
18270         
18271         this.startValue = this.getValue();
18272         
18273         if(this.groupId){
18274             Roo.bootstrap.CheckBox.register(this);
18275         }
18276     },
18277     
18278     onClick : function()
18279     {   
18280         this.setChecked(!this.checked);
18281     },
18282     
18283     setChecked : function(state,suppressEvent)
18284     {
18285         this.startValue = this.getValue();
18286         
18287         if(this.inputType == 'radio'){
18288             
18289             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18290                 e.dom.checked = false;
18291             });
18292             
18293             this.inputEl().dom.checked = true;
18294             
18295             this.inputEl().dom.value = this.inputValue;
18296             
18297             if(suppressEvent !== true){
18298                 this.fireEvent('check', this, true);
18299             }
18300             
18301             this.validate();
18302             
18303             return;
18304         }
18305         
18306         this.checked = state;
18307         
18308         this.inputEl().dom.checked = state;
18309         
18310         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18311         
18312         if(suppressEvent !== true){
18313             this.fireEvent('check', this, state);
18314         }
18315         
18316         this.validate();
18317     },
18318     
18319     getValue : function()
18320     {
18321         if(this.inputType == 'radio'){
18322             return this.getGroupValue();
18323         }
18324         
18325         return this.inputEl().getValue();
18326         
18327     },
18328     
18329     getGroupValue : function()
18330     {
18331         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18332             return '';
18333         }
18334         
18335         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18336     },
18337     
18338     setValue : function(v,suppressEvent)
18339     {
18340         if(this.inputType == 'radio'){
18341             this.setGroupValue(v, suppressEvent);
18342             return;
18343         }
18344         
18345         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18346         
18347         this.validate();
18348     },
18349     
18350     setGroupValue : function(v, suppressEvent)
18351     {
18352         this.startValue = this.getValue();
18353         
18354         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18355             e.dom.checked = false;
18356             
18357             if(e.dom.value == v){
18358                 e.dom.checked = true;
18359             }
18360         });
18361         
18362         if(suppressEvent !== true){
18363             this.fireEvent('check', this, true);
18364         }
18365
18366         this.validate();
18367         
18368         return;
18369     },
18370     
18371     validate : function()
18372     {
18373         if(
18374                 this.disabled || 
18375                 (this.inputType == 'radio' && this.validateRadio()) ||
18376                 (this.inputType == 'checkbox' && this.validateCheckbox())
18377         ){
18378             this.markValid();
18379             return true;
18380         }
18381         
18382         this.markInvalid();
18383         return false;
18384     },
18385     
18386     validateRadio : function()
18387     {
18388         var valid = false;
18389         
18390         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18391             if(!e.dom.checked){
18392                 return;
18393             }
18394             
18395             valid = true;
18396             
18397             return false;
18398         });
18399         
18400         return valid;
18401     },
18402     
18403     validateCheckbox : function()
18404     {
18405         if(!this.groupId){
18406             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18407         }
18408         
18409         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18410         
18411         if(!group){
18412             return false;
18413         }
18414         
18415         var r = false;
18416         
18417         for(var i in group){
18418             if(r){
18419                 break;
18420             }
18421             
18422             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18423         }
18424         
18425         return r;
18426     },
18427     
18428     /**
18429      * Mark this field as valid
18430      */
18431     markValid : function()
18432     {
18433         if(this.allowBlank){
18434             return;
18435         }
18436         
18437         var _this = this;
18438         
18439         this.fireEvent('valid', this);
18440         
18441         if(this.inputType == 'radio'){
18442             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18443                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18444                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18445             });
18446             
18447             return;
18448         }
18449         
18450         if(!this.groupId){
18451             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18452             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18453             return;
18454         }
18455         
18456         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18457             
18458         if(!group){
18459             return;
18460         }
18461         
18462         for(var i in group){
18463             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18464             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18465         }
18466     },
18467     
18468      /**
18469      * Mark this field as invalid
18470      * @param {String} msg The validation message
18471      */
18472     markInvalid : function(msg)
18473     {
18474         if(this.allowBlank){
18475             return;
18476         }
18477         
18478         var _this = this;
18479         
18480         this.fireEvent('invalid', this, msg);
18481         
18482         if(this.inputType == 'radio'){
18483             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18484                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18485                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18486             });
18487             
18488             return;
18489         }
18490         
18491         if(!this.groupId){
18492             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18493             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18494             return;
18495         }
18496         
18497         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18498             
18499         if(!group){
18500             return;
18501         }
18502         
18503         for(var i in group){
18504             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18505             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18506         }
18507         
18508     }
18509     
18510 });
18511
18512 Roo.apply(Roo.bootstrap.CheckBox, {
18513     
18514     groups: {},
18515     
18516      /**
18517     * register a CheckBox Group
18518     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18519     */
18520     register : function(checkbox)
18521     {
18522         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18523             this.groups[checkbox.groupId] = {};
18524         }
18525         
18526         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18527             return;
18528         }
18529         
18530         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18531         
18532     },
18533     /**
18534     * fetch a CheckBox Group based on the group ID
18535     * @param {string} the group ID
18536     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18537     */
18538     get: function(groupId) {
18539         if (typeof(this.groups[groupId]) == 'undefined') {
18540             return false;
18541         }
18542         
18543         return this.groups[groupId] ;
18544     }
18545     
18546     
18547 });
18548 /*
18549  * - LGPL
18550  *
18551  * Radio
18552  *
18553  *
18554  * not inline
18555  *<div class="radio">
18556   <label>
18557     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18558     Option one is this and that&mdash;be sure to include why it's great
18559   </label>
18560 </div>
18561  *
18562  *
18563  *inline
18564  *<span>
18565  *<label class="radio-inline">fieldLabel</label>
18566  *<label class="radio-inline">
18567   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18568 </label>
18569 <span>
18570  * 
18571  * 
18572  */
18573
18574 /**
18575  * @class Roo.bootstrap.Radio
18576  * @extends Roo.bootstrap.CheckBox
18577  * Bootstrap Radio class
18578
18579  * @constructor
18580  * Create a new Radio
18581  * @param {Object} config The config object
18582  */
18583
18584 Roo.bootstrap.Radio = function(config){
18585     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18586    
18587 };
18588
18589 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18590     
18591     inputType: 'radio',
18592     inputValue: '',
18593     valueOff: '',
18594     
18595     getAutoCreate : function()
18596     {
18597         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18598         align = align || 'left'; // default...
18599         
18600         
18601         
18602         var id = Roo.id();
18603         
18604         var cfg = {
18605                 tag : this.inline ? 'span' : 'div',
18606                 cls : '',
18607                 cn : []
18608         };
18609         
18610         var inline = this.inline ? ' radio-inline' : '';
18611         
18612         var lbl = {
18613                 tag: 'label' ,
18614                 // does not need for, as we wrap the input with it..
18615                 'for' : id,
18616                 cls : 'control-label box-label' + inline,
18617                 cn : []
18618         };
18619         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18620         
18621         var fieldLabel = {
18622             tag: 'label' ,
18623             //cls : 'control-label' + inline,
18624             html : this.fieldLabel,
18625             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18626         };
18627         
18628  
18629         
18630         
18631         var input =  {
18632             tag: 'input',
18633             id : id,
18634             type : this.inputType,
18635             //value : (!this.checked) ? this.valueOff : this.inputValue,
18636             value : this.inputValue,
18637             cls : 'roo-radio',
18638             placeholder : this.placeholder || '' // ?? needed????
18639             
18640         };
18641         if (this.weight) { // Validity check?
18642             input.cls += " radio-" + this.weight;
18643         }
18644         if (this.disabled) {
18645             input.disabled=true;
18646         }
18647         
18648         if(this.checked){
18649             input.checked = this.checked;
18650         }
18651         
18652         if (this.name) {
18653             input.name = this.name;
18654         }
18655         
18656         if (this.size) {
18657             input.cls += ' input-' + this.size;
18658         }
18659         
18660         //?? can span's inline have a width??
18661         
18662         var settings=this;
18663         ['xs','sm','md','lg'].map(function(size){
18664             if (settings[size]) {
18665                 cfg.cls += ' col-' + size + '-' + settings[size];
18666             }
18667         });
18668         
18669         var inputblock = input;
18670         
18671         if (this.before || this.after) {
18672             
18673             inputblock = {
18674                 cls : 'input-group',
18675                 tag : 'span',
18676                 cn :  [] 
18677             };
18678             if (this.before) {
18679                 inputblock.cn.push({
18680                     tag :'span',
18681                     cls : 'input-group-addon',
18682                     html : this.before
18683                 });
18684             }
18685             inputblock.cn.push(input);
18686             if (this.after) {
18687                 inputblock.cn.push({
18688                     tag :'span',
18689                     cls : 'input-group-addon',
18690                     html : this.after
18691                 });
18692             }
18693             
18694         };
18695         
18696         
18697         if (this.fieldLabel && this.fieldLabel.length) {
18698             cfg.cn.push(fieldLabel);
18699         }
18700        
18701         // normal bootstrap puts the input inside the label.
18702         // however with our styled version - it has to go after the input.
18703        
18704         //lbl.cn.push(inputblock);
18705         
18706         var lblwrap =  {
18707             tag: 'span',
18708             cls: 'radio' + inline,
18709             cn: [
18710                 inputblock,
18711                 lbl
18712             ]
18713         };
18714         
18715         cfg.cn.push( lblwrap);
18716         
18717         if(this.boxLabel){
18718             lbl.cn.push({
18719                 tag: 'span',
18720                 html: this.boxLabel
18721             })
18722         }
18723          
18724         
18725         return cfg;
18726         
18727     },
18728     
18729     initEvents : function()
18730     {
18731 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18732         
18733         this.inputEl().on('click', this.onClick,  this);
18734         if (this.boxLabel) {
18735             Roo.log('find label')
18736             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18737         }
18738         
18739     },
18740     
18741     inputEl: function ()
18742     {
18743         return this.el.select('input.roo-radio',true).first();
18744     },
18745     onClick : function()
18746     {   
18747         Roo.log("click");
18748         this.setChecked(true);
18749     },
18750     
18751     setChecked : function(state,suppressEvent)
18752     {
18753         if(state){
18754             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18755                 v.dom.checked = false;
18756             });
18757         }
18758         Roo.log(this.inputEl().dom);
18759         this.checked = state;
18760         this.inputEl().dom.checked = state;
18761         
18762         if(suppressEvent !== true){
18763             this.fireEvent('check', this, state);
18764         }
18765         
18766         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18767         
18768     },
18769     
18770     getGroupValue : function()
18771     {
18772         var value = '';
18773         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18774             if(v.dom.checked == true){
18775                 value = v.dom.value;
18776             }
18777         });
18778         
18779         return value;
18780     },
18781     
18782     /**
18783      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18784      * @return {Mixed} value The field value
18785      */
18786     getValue : function(){
18787         return this.getGroupValue();
18788     }
18789     
18790 });
18791
18792  
18793 //<script type="text/javascript">
18794
18795 /*
18796  * Based  Ext JS Library 1.1.1
18797  * Copyright(c) 2006-2007, Ext JS, LLC.
18798  * LGPL
18799  *
18800  */
18801  
18802 /**
18803  * @class Roo.HtmlEditorCore
18804  * @extends Roo.Component
18805  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18806  *
18807  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18808  */
18809
18810 Roo.HtmlEditorCore = function(config){
18811     
18812     
18813     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18814     
18815     
18816     this.addEvents({
18817         /**
18818          * @event initialize
18819          * Fires when the editor is fully initialized (including the iframe)
18820          * @param {Roo.HtmlEditorCore} this
18821          */
18822         initialize: true,
18823         /**
18824          * @event activate
18825          * Fires when the editor is first receives the focus. Any insertion must wait
18826          * until after this event.
18827          * @param {Roo.HtmlEditorCore} this
18828          */
18829         activate: true,
18830          /**
18831          * @event beforesync
18832          * Fires before the textarea is updated with content from the editor iframe. Return false
18833          * to cancel the sync.
18834          * @param {Roo.HtmlEditorCore} this
18835          * @param {String} html
18836          */
18837         beforesync: true,
18838          /**
18839          * @event beforepush
18840          * Fires before the iframe editor is updated with content from the textarea. Return false
18841          * to cancel the push.
18842          * @param {Roo.HtmlEditorCore} this
18843          * @param {String} html
18844          */
18845         beforepush: true,
18846          /**
18847          * @event sync
18848          * Fires when the textarea is updated with content from the editor iframe.
18849          * @param {Roo.HtmlEditorCore} this
18850          * @param {String} html
18851          */
18852         sync: true,
18853          /**
18854          * @event push
18855          * Fires when the iframe editor is updated with content from the textarea.
18856          * @param {Roo.HtmlEditorCore} this
18857          * @param {String} html
18858          */
18859         push: true,
18860         
18861         /**
18862          * @event editorevent
18863          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18864          * @param {Roo.HtmlEditorCore} this
18865          */
18866         editorevent: true
18867         
18868     });
18869     
18870     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18871     
18872     // defaults : white / black...
18873     this.applyBlacklists();
18874     
18875     
18876     
18877 };
18878
18879
18880 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18881
18882
18883      /**
18884      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18885      */
18886     
18887     owner : false,
18888     
18889      /**
18890      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18891      *                        Roo.resizable.
18892      */
18893     resizable : false,
18894      /**
18895      * @cfg {Number} height (in pixels)
18896      */   
18897     height: 300,
18898    /**
18899      * @cfg {Number} width (in pixels)
18900      */   
18901     width: 500,
18902     
18903     /**
18904      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18905      * 
18906      */
18907     stylesheets: false,
18908     
18909     // id of frame..
18910     frameId: false,
18911     
18912     // private properties
18913     validationEvent : false,
18914     deferHeight: true,
18915     initialized : false,
18916     activated : false,
18917     sourceEditMode : false,
18918     onFocus : Roo.emptyFn,
18919     iframePad:3,
18920     hideMode:'offsets',
18921     
18922     clearUp: true,
18923     
18924     // blacklist + whitelisted elements..
18925     black: false,
18926     white: false,
18927      
18928     
18929
18930     /**
18931      * Protected method that will not generally be called directly. It
18932      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18933      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18934      */
18935     getDocMarkup : function(){
18936         // body styles..
18937         var st = '';
18938         
18939         // inherit styels from page...?? 
18940         if (this.stylesheets === false) {
18941             
18942             Roo.get(document.head).select('style').each(function(node) {
18943                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18944             });
18945             
18946             Roo.get(document.head).select('link').each(function(node) { 
18947                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18948             });
18949             
18950         } else if (!this.stylesheets.length) {
18951                 // simple..
18952                 st = '<style type="text/css">' +
18953                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18954                    '</style>';
18955         } else { 
18956             
18957         }
18958         
18959         st +=  '<style type="text/css">' +
18960             'IMG { cursor: pointer } ' +
18961         '</style>';
18962
18963         
18964         return '<html><head>' + st  +
18965             //<style type="text/css">' +
18966             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18967             //'</style>' +
18968             ' </head><body class="roo-htmleditor-body"></body></html>';
18969     },
18970
18971     // private
18972     onRender : function(ct, position)
18973     {
18974         var _t = this;
18975         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18976         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18977         
18978         
18979         this.el.dom.style.border = '0 none';
18980         this.el.dom.setAttribute('tabIndex', -1);
18981         this.el.addClass('x-hidden hide');
18982         
18983         
18984         
18985         if(Roo.isIE){ // fix IE 1px bogus margin
18986             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18987         }
18988        
18989         
18990         this.frameId = Roo.id();
18991         
18992          
18993         
18994         var iframe = this.owner.wrap.createChild({
18995             tag: 'iframe',
18996             cls: 'form-control', // bootstrap..
18997             id: this.frameId,
18998             name: this.frameId,
18999             frameBorder : 'no',
19000             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19001         }, this.el
19002         );
19003         
19004         
19005         this.iframe = iframe.dom;
19006
19007          this.assignDocWin();
19008         
19009         this.doc.designMode = 'on';
19010        
19011         this.doc.open();
19012         this.doc.write(this.getDocMarkup());
19013         this.doc.close();
19014
19015         
19016         var task = { // must defer to wait for browser to be ready
19017             run : function(){
19018                 //console.log("run task?" + this.doc.readyState);
19019                 this.assignDocWin();
19020                 if(this.doc.body || this.doc.readyState == 'complete'){
19021                     try {
19022                         this.doc.designMode="on";
19023                     } catch (e) {
19024                         return;
19025                     }
19026                     Roo.TaskMgr.stop(task);
19027                     this.initEditor.defer(10, this);
19028                 }
19029             },
19030             interval : 10,
19031             duration: 10000,
19032             scope: this
19033         };
19034         Roo.TaskMgr.start(task);
19035
19036     },
19037
19038     // private
19039     onResize : function(w, h)
19040     {
19041          Roo.log('resize: ' +w + ',' + h );
19042         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19043         if(!this.iframe){
19044             return;
19045         }
19046         if(typeof w == 'number'){
19047             
19048             this.iframe.style.width = w + 'px';
19049         }
19050         if(typeof h == 'number'){
19051             
19052             this.iframe.style.height = h + 'px';
19053             if(this.doc){
19054                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19055             }
19056         }
19057         
19058     },
19059
19060     /**
19061      * Toggles the editor between standard and source edit mode.
19062      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19063      */
19064     toggleSourceEdit : function(sourceEditMode){
19065         
19066         this.sourceEditMode = sourceEditMode === true;
19067         
19068         if(this.sourceEditMode){
19069  
19070             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19071             
19072         }else{
19073             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19074             //this.iframe.className = '';
19075             this.deferFocus();
19076         }
19077         //this.setSize(this.owner.wrap.getSize());
19078         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19079     },
19080
19081     
19082   
19083
19084     /**
19085      * Protected method that will not generally be called directly. If you need/want
19086      * custom HTML cleanup, this is the method you should override.
19087      * @param {String} html The HTML to be cleaned
19088      * return {String} The cleaned HTML
19089      */
19090     cleanHtml : function(html){
19091         html = String(html);
19092         if(html.length > 5){
19093             if(Roo.isSafari){ // strip safari nonsense
19094                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19095             }
19096         }
19097         if(html == '&nbsp;'){
19098             html = '';
19099         }
19100         return html;
19101     },
19102
19103     /**
19104      * HTML Editor -> Textarea
19105      * Protected method that will not generally be called directly. Syncs the contents
19106      * of the editor iframe with the textarea.
19107      */
19108     syncValue : function(){
19109         if(this.initialized){
19110             var bd = (this.doc.body || this.doc.documentElement);
19111             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19112             var html = bd.innerHTML;
19113             if(Roo.isSafari){
19114                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19115                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19116                 if(m && m[1]){
19117                     html = '<div style="'+m[0]+'">' + html + '</div>';
19118                 }
19119             }
19120             html = this.cleanHtml(html);
19121             // fix up the special chars.. normaly like back quotes in word...
19122             // however we do not want to do this with chinese..
19123             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19124                 var cc = b.charCodeAt();
19125                 if (
19126                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19127                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19128                     (cc >= 0xf900 && cc < 0xfb00 )
19129                 ) {
19130                         return b;
19131                 }
19132                 return "&#"+cc+";" 
19133             });
19134             if(this.owner.fireEvent('beforesync', this, html) !== false){
19135                 this.el.dom.value = html;
19136                 this.owner.fireEvent('sync', this, html);
19137             }
19138         }
19139     },
19140
19141     /**
19142      * Protected method that will not generally be called directly. Pushes the value of the textarea
19143      * into the iframe editor.
19144      */
19145     pushValue : function(){
19146         if(this.initialized){
19147             var v = this.el.dom.value.trim();
19148             
19149 //            if(v.length < 1){
19150 //                v = '&#160;';
19151 //            }
19152             
19153             if(this.owner.fireEvent('beforepush', this, v) !== false){
19154                 var d = (this.doc.body || this.doc.documentElement);
19155                 d.innerHTML = v;
19156                 this.cleanUpPaste();
19157                 this.el.dom.value = d.innerHTML;
19158                 this.owner.fireEvent('push', this, v);
19159             }
19160         }
19161     },
19162
19163     // private
19164     deferFocus : function(){
19165         this.focus.defer(10, this);
19166     },
19167
19168     // doc'ed in Field
19169     focus : function(){
19170         if(this.win && !this.sourceEditMode){
19171             this.win.focus();
19172         }else{
19173             this.el.focus();
19174         }
19175     },
19176     
19177     assignDocWin: function()
19178     {
19179         var iframe = this.iframe;
19180         
19181          if(Roo.isIE){
19182             this.doc = iframe.contentWindow.document;
19183             this.win = iframe.contentWindow;
19184         } else {
19185 //            if (!Roo.get(this.frameId)) {
19186 //                return;
19187 //            }
19188 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19189 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19190             
19191             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19192                 return;
19193             }
19194             
19195             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19196             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19197         }
19198     },
19199     
19200     // private
19201     initEditor : function(){
19202         //console.log("INIT EDITOR");
19203         this.assignDocWin();
19204         
19205         
19206         
19207         this.doc.designMode="on";
19208         this.doc.open();
19209         this.doc.write(this.getDocMarkup());
19210         this.doc.close();
19211         
19212         var dbody = (this.doc.body || this.doc.documentElement);
19213         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19214         // this copies styles from the containing element into thsi one..
19215         // not sure why we need all of this..
19216         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19217         
19218         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19219         //ss['background-attachment'] = 'fixed'; // w3c
19220         dbody.bgProperties = 'fixed'; // ie
19221         //Roo.DomHelper.applyStyles(dbody, ss);
19222         Roo.EventManager.on(this.doc, {
19223             //'mousedown': this.onEditorEvent,
19224             'mouseup': this.onEditorEvent,
19225             'dblclick': this.onEditorEvent,
19226             'click': this.onEditorEvent,
19227             'keyup': this.onEditorEvent,
19228             buffer:100,
19229             scope: this
19230         });
19231         if(Roo.isGecko){
19232             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19233         }
19234         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19235             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19236         }
19237         this.initialized = true;
19238
19239         this.owner.fireEvent('initialize', this);
19240         this.pushValue();
19241     },
19242
19243     // private
19244     onDestroy : function(){
19245         
19246         
19247         
19248         if(this.rendered){
19249             
19250             //for (var i =0; i < this.toolbars.length;i++) {
19251             //    // fixme - ask toolbars for heights?
19252             //    this.toolbars[i].onDestroy();
19253            // }
19254             
19255             //this.wrap.dom.innerHTML = '';
19256             //this.wrap.remove();
19257         }
19258     },
19259
19260     // private
19261     onFirstFocus : function(){
19262         
19263         this.assignDocWin();
19264         
19265         
19266         this.activated = true;
19267          
19268     
19269         if(Roo.isGecko){ // prevent silly gecko errors
19270             this.win.focus();
19271             var s = this.win.getSelection();
19272             if(!s.focusNode || s.focusNode.nodeType != 3){
19273                 var r = s.getRangeAt(0);
19274                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19275                 r.collapse(true);
19276                 this.deferFocus();
19277             }
19278             try{
19279                 this.execCmd('useCSS', true);
19280                 this.execCmd('styleWithCSS', false);
19281             }catch(e){}
19282         }
19283         this.owner.fireEvent('activate', this);
19284     },
19285
19286     // private
19287     adjustFont: function(btn){
19288         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19289         //if(Roo.isSafari){ // safari
19290         //    adjust *= 2;
19291        // }
19292         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19293         if(Roo.isSafari){ // safari
19294             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19295             v =  (v < 10) ? 10 : v;
19296             v =  (v > 48) ? 48 : v;
19297             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19298             
19299         }
19300         
19301         
19302         v = Math.max(1, v+adjust);
19303         
19304         this.execCmd('FontSize', v  );
19305     },
19306
19307     onEditorEvent : function(e)
19308     {
19309         this.owner.fireEvent('editorevent', this, e);
19310       //  this.updateToolbar();
19311         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19312     },
19313
19314     insertTag : function(tg)
19315     {
19316         // could be a bit smarter... -> wrap the current selected tRoo..
19317         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19318             
19319             range = this.createRange(this.getSelection());
19320             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19321             wrappingNode.appendChild(range.extractContents());
19322             range.insertNode(wrappingNode);
19323
19324             return;
19325             
19326             
19327             
19328         }
19329         this.execCmd("formatblock",   tg);
19330         
19331     },
19332     
19333     insertText : function(txt)
19334     {
19335         
19336         
19337         var range = this.createRange();
19338         range.deleteContents();
19339                //alert(Sender.getAttribute('label'));
19340                
19341         range.insertNode(this.doc.createTextNode(txt));
19342     } ,
19343     
19344      
19345
19346     /**
19347      * Executes a Midas editor command on the editor document and performs necessary focus and
19348      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19349      * @param {String} cmd The Midas command
19350      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19351      */
19352     relayCmd : function(cmd, value){
19353         this.win.focus();
19354         this.execCmd(cmd, value);
19355         this.owner.fireEvent('editorevent', this);
19356         //this.updateToolbar();
19357         this.owner.deferFocus();
19358     },
19359
19360     /**
19361      * Executes a Midas editor command directly on the editor document.
19362      * For visual commands, you should use {@link #relayCmd} instead.
19363      * <b>This should only be called after the editor is initialized.</b>
19364      * @param {String} cmd The Midas command
19365      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19366      */
19367     execCmd : function(cmd, value){
19368         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19369         this.syncValue();
19370     },
19371  
19372  
19373    
19374     /**
19375      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19376      * to insert tRoo.
19377      * @param {String} text | dom node.. 
19378      */
19379     insertAtCursor : function(text)
19380     {
19381         
19382         
19383         
19384         if(!this.activated){
19385             return;
19386         }
19387         /*
19388         if(Roo.isIE){
19389             this.win.focus();
19390             var r = this.doc.selection.createRange();
19391             if(r){
19392                 r.collapse(true);
19393                 r.pasteHTML(text);
19394                 this.syncValue();
19395                 this.deferFocus();
19396             
19397             }
19398             return;
19399         }
19400         */
19401         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19402             this.win.focus();
19403             
19404             
19405             // from jquery ui (MIT licenced)
19406             var range, node;
19407             var win = this.win;
19408             
19409             if (win.getSelection && win.getSelection().getRangeAt) {
19410                 range = win.getSelection().getRangeAt(0);
19411                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19412                 range.insertNode(node);
19413             } else if (win.document.selection && win.document.selection.createRange) {
19414                 // no firefox support
19415                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19416                 win.document.selection.createRange().pasteHTML(txt);
19417             } else {
19418                 // no firefox support
19419                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19420                 this.execCmd('InsertHTML', txt);
19421             } 
19422             
19423             this.syncValue();
19424             
19425             this.deferFocus();
19426         }
19427     },
19428  // private
19429     mozKeyPress : function(e){
19430         if(e.ctrlKey){
19431             var c = e.getCharCode(), cmd;
19432           
19433             if(c > 0){
19434                 c = String.fromCharCode(c).toLowerCase();
19435                 switch(c){
19436                     case 'b':
19437                         cmd = 'bold';
19438                         break;
19439                     case 'i':
19440                         cmd = 'italic';
19441                         break;
19442                     
19443                     case 'u':
19444                         cmd = 'underline';
19445                         break;
19446                     
19447                     case 'v':
19448                         this.cleanUpPaste.defer(100, this);
19449                         return;
19450                         
19451                 }
19452                 if(cmd){
19453                     this.win.focus();
19454                     this.execCmd(cmd);
19455                     this.deferFocus();
19456                     e.preventDefault();
19457                 }
19458                 
19459             }
19460         }
19461     },
19462
19463     // private
19464     fixKeys : function(){ // load time branching for fastest keydown performance
19465         if(Roo.isIE){
19466             return function(e){
19467                 var k = e.getKey(), r;
19468                 if(k == e.TAB){
19469                     e.stopEvent();
19470                     r = this.doc.selection.createRange();
19471                     if(r){
19472                         r.collapse(true);
19473                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19474                         this.deferFocus();
19475                     }
19476                     return;
19477                 }
19478                 
19479                 if(k == e.ENTER){
19480                     r = this.doc.selection.createRange();
19481                     if(r){
19482                         var target = r.parentElement();
19483                         if(!target || target.tagName.toLowerCase() != 'li'){
19484                             e.stopEvent();
19485                             r.pasteHTML('<br />');
19486                             r.collapse(false);
19487                             r.select();
19488                         }
19489                     }
19490                 }
19491                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19492                     this.cleanUpPaste.defer(100, this);
19493                     return;
19494                 }
19495                 
19496                 
19497             };
19498         }else if(Roo.isOpera){
19499             return function(e){
19500                 var k = e.getKey();
19501                 if(k == e.TAB){
19502                     e.stopEvent();
19503                     this.win.focus();
19504                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19505                     this.deferFocus();
19506                 }
19507                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19508                     this.cleanUpPaste.defer(100, this);
19509                     return;
19510                 }
19511                 
19512             };
19513         }else if(Roo.isSafari){
19514             return function(e){
19515                 var k = e.getKey();
19516                 
19517                 if(k == e.TAB){
19518                     e.stopEvent();
19519                     this.execCmd('InsertText','\t');
19520                     this.deferFocus();
19521                     return;
19522                 }
19523                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19524                     this.cleanUpPaste.defer(100, this);
19525                     return;
19526                 }
19527                 
19528              };
19529         }
19530     }(),
19531     
19532     getAllAncestors: function()
19533     {
19534         var p = this.getSelectedNode();
19535         var a = [];
19536         if (!p) {
19537             a.push(p); // push blank onto stack..
19538             p = this.getParentElement();
19539         }
19540         
19541         
19542         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19543             a.push(p);
19544             p = p.parentNode;
19545         }
19546         a.push(this.doc.body);
19547         return a;
19548     },
19549     lastSel : false,
19550     lastSelNode : false,
19551     
19552     
19553     getSelection : function() 
19554     {
19555         this.assignDocWin();
19556         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19557     },
19558     
19559     getSelectedNode: function() 
19560     {
19561         // this may only work on Gecko!!!
19562         
19563         // should we cache this!!!!
19564         
19565         
19566         
19567          
19568         var range = this.createRange(this.getSelection()).cloneRange();
19569         
19570         if (Roo.isIE) {
19571             var parent = range.parentElement();
19572             while (true) {
19573                 var testRange = range.duplicate();
19574                 testRange.moveToElementText(parent);
19575                 if (testRange.inRange(range)) {
19576                     break;
19577                 }
19578                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19579                     break;
19580                 }
19581                 parent = parent.parentElement;
19582             }
19583             return parent;
19584         }
19585         
19586         // is ancestor a text element.
19587         var ac =  range.commonAncestorContainer;
19588         if (ac.nodeType == 3) {
19589             ac = ac.parentNode;
19590         }
19591         
19592         var ar = ac.childNodes;
19593          
19594         var nodes = [];
19595         var other_nodes = [];
19596         var has_other_nodes = false;
19597         for (var i=0;i<ar.length;i++) {
19598             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19599                 continue;
19600             }
19601             // fullly contained node.
19602             
19603             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19604                 nodes.push(ar[i]);
19605                 continue;
19606             }
19607             
19608             // probably selected..
19609             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19610                 other_nodes.push(ar[i]);
19611                 continue;
19612             }
19613             // outer..
19614             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19615                 continue;
19616             }
19617             
19618             
19619             has_other_nodes = true;
19620         }
19621         if (!nodes.length && other_nodes.length) {
19622             nodes= other_nodes;
19623         }
19624         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19625             return false;
19626         }
19627         
19628         return nodes[0];
19629     },
19630     createRange: function(sel)
19631     {
19632         // this has strange effects when using with 
19633         // top toolbar - not sure if it's a great idea.
19634         //this.editor.contentWindow.focus();
19635         if (typeof sel != "undefined") {
19636             try {
19637                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19638             } catch(e) {
19639                 return this.doc.createRange();
19640             }
19641         } else {
19642             return this.doc.createRange();
19643         }
19644     },
19645     getParentElement: function()
19646     {
19647         
19648         this.assignDocWin();
19649         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19650         
19651         var range = this.createRange(sel);
19652          
19653         try {
19654             var p = range.commonAncestorContainer;
19655             while (p.nodeType == 3) { // text node
19656                 p = p.parentNode;
19657             }
19658             return p;
19659         } catch (e) {
19660             return null;
19661         }
19662     
19663     },
19664     /***
19665      *
19666      * Range intersection.. the hard stuff...
19667      *  '-1' = before
19668      *  '0' = hits..
19669      *  '1' = after.
19670      *         [ -- selected range --- ]
19671      *   [fail]                        [fail]
19672      *
19673      *    basically..
19674      *      if end is before start or  hits it. fail.
19675      *      if start is after end or hits it fail.
19676      *
19677      *   if either hits (but other is outside. - then it's not 
19678      *   
19679      *    
19680      **/
19681     
19682     
19683     // @see http://www.thismuchiknow.co.uk/?p=64.
19684     rangeIntersectsNode : function(range, node)
19685     {
19686         var nodeRange = node.ownerDocument.createRange();
19687         try {
19688             nodeRange.selectNode(node);
19689         } catch (e) {
19690             nodeRange.selectNodeContents(node);
19691         }
19692     
19693         var rangeStartRange = range.cloneRange();
19694         rangeStartRange.collapse(true);
19695     
19696         var rangeEndRange = range.cloneRange();
19697         rangeEndRange.collapse(false);
19698     
19699         var nodeStartRange = nodeRange.cloneRange();
19700         nodeStartRange.collapse(true);
19701     
19702         var nodeEndRange = nodeRange.cloneRange();
19703         nodeEndRange.collapse(false);
19704     
19705         return rangeStartRange.compareBoundaryPoints(
19706                  Range.START_TO_START, nodeEndRange) == -1 &&
19707                rangeEndRange.compareBoundaryPoints(
19708                  Range.START_TO_START, nodeStartRange) == 1;
19709         
19710          
19711     },
19712     rangeCompareNode : function(range, node)
19713     {
19714         var nodeRange = node.ownerDocument.createRange();
19715         try {
19716             nodeRange.selectNode(node);
19717         } catch (e) {
19718             nodeRange.selectNodeContents(node);
19719         }
19720         
19721         
19722         range.collapse(true);
19723     
19724         nodeRange.collapse(true);
19725      
19726         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19727         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19728          
19729         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19730         
19731         var nodeIsBefore   =  ss == 1;
19732         var nodeIsAfter    = ee == -1;
19733         
19734         if (nodeIsBefore && nodeIsAfter)
19735             return 0; // outer
19736         if (!nodeIsBefore && nodeIsAfter)
19737             return 1; //right trailed.
19738         
19739         if (nodeIsBefore && !nodeIsAfter)
19740             return 2;  // left trailed.
19741         // fully contined.
19742         return 3;
19743     },
19744
19745     // private? - in a new class?
19746     cleanUpPaste :  function()
19747     {
19748         // cleans up the whole document..
19749         Roo.log('cleanuppaste');
19750         
19751         this.cleanUpChildren(this.doc.body);
19752         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19753         if (clean != this.doc.body.innerHTML) {
19754             this.doc.body.innerHTML = clean;
19755         }
19756         
19757     },
19758     
19759     cleanWordChars : function(input) {// change the chars to hex code
19760         var he = Roo.HtmlEditorCore;
19761         
19762         var output = input;
19763         Roo.each(he.swapCodes, function(sw) { 
19764             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19765             
19766             output = output.replace(swapper, sw[1]);
19767         });
19768         
19769         return output;
19770     },
19771     
19772     
19773     cleanUpChildren : function (n)
19774     {
19775         if (!n.childNodes.length) {
19776             return;
19777         }
19778         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19779            this.cleanUpChild(n.childNodes[i]);
19780         }
19781     },
19782     
19783     
19784         
19785     
19786     cleanUpChild : function (node)
19787     {
19788         var ed = this;
19789         //console.log(node);
19790         if (node.nodeName == "#text") {
19791             // clean up silly Windows -- stuff?
19792             return; 
19793         }
19794         if (node.nodeName == "#comment") {
19795             node.parentNode.removeChild(node);
19796             // clean up silly Windows -- stuff?
19797             return; 
19798         }
19799         var lcname = node.tagName.toLowerCase();
19800         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19801         // whitelist of tags..
19802         
19803         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19804             // remove node.
19805             node.parentNode.removeChild(node);
19806             return;
19807             
19808         }
19809         
19810         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19811         
19812         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19813         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19814         
19815         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19816         //    remove_keep_children = true;
19817         //}
19818         
19819         if (remove_keep_children) {
19820             this.cleanUpChildren(node);
19821             // inserts everything just before this node...
19822             while (node.childNodes.length) {
19823                 var cn = node.childNodes[0];
19824                 node.removeChild(cn);
19825                 node.parentNode.insertBefore(cn, node);
19826             }
19827             node.parentNode.removeChild(node);
19828             return;
19829         }
19830         
19831         if (!node.attributes || !node.attributes.length) {
19832             this.cleanUpChildren(node);
19833             return;
19834         }
19835         
19836         function cleanAttr(n,v)
19837         {
19838             
19839             if (v.match(/^\./) || v.match(/^\//)) {
19840                 return;
19841             }
19842             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19843                 return;
19844             }
19845             if (v.match(/^#/)) {
19846                 return;
19847             }
19848 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19849             node.removeAttribute(n);
19850             
19851         }
19852         
19853         var cwhite = this.cwhite;
19854         var cblack = this.cblack;
19855             
19856         function cleanStyle(n,v)
19857         {
19858             if (v.match(/expression/)) { //XSS?? should we even bother..
19859                 node.removeAttribute(n);
19860                 return;
19861             }
19862             
19863             var parts = v.split(/;/);
19864             var clean = [];
19865             
19866             Roo.each(parts, function(p) {
19867                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19868                 if (!p.length) {
19869                     return true;
19870                 }
19871                 var l = p.split(':').shift().replace(/\s+/g,'');
19872                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19873                 
19874                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19875 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19876                     //node.removeAttribute(n);
19877                     return true;
19878                 }
19879                 //Roo.log()
19880                 // only allow 'c whitelisted system attributes'
19881                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19882 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19883                     //node.removeAttribute(n);
19884                     return true;
19885                 }
19886                 
19887                 
19888                  
19889                 
19890                 clean.push(p);
19891                 return true;
19892             });
19893             if (clean.length) { 
19894                 node.setAttribute(n, clean.join(';'));
19895             } else {
19896                 node.removeAttribute(n);
19897             }
19898             
19899         }
19900         
19901         
19902         for (var i = node.attributes.length-1; i > -1 ; i--) {
19903             var a = node.attributes[i];
19904             //console.log(a);
19905             
19906             if (a.name.toLowerCase().substr(0,2)=='on')  {
19907                 node.removeAttribute(a.name);
19908                 continue;
19909             }
19910             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19911                 node.removeAttribute(a.name);
19912                 continue;
19913             }
19914             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19915                 cleanAttr(a.name,a.value); // fixme..
19916                 continue;
19917             }
19918             if (a.name == 'style') {
19919                 cleanStyle(a.name,a.value);
19920                 continue;
19921             }
19922             /// clean up MS crap..
19923             // tecnically this should be a list of valid class'es..
19924             
19925             
19926             if (a.name == 'class') {
19927                 if (a.value.match(/^Mso/)) {
19928                     node.className = '';
19929                 }
19930                 
19931                 if (a.value.match(/body/)) {
19932                     node.className = '';
19933                 }
19934                 continue;
19935             }
19936             
19937             // style cleanup!?
19938             // class cleanup?
19939             
19940         }
19941         
19942         
19943         this.cleanUpChildren(node);
19944         
19945         
19946     },
19947     
19948     /**
19949      * Clean up MS wordisms...
19950      */
19951     cleanWord : function(node)
19952     {
19953         
19954         
19955         if (!node) {
19956             this.cleanWord(this.doc.body);
19957             return;
19958         }
19959         if (node.nodeName == "#text") {
19960             // clean up silly Windows -- stuff?
19961             return; 
19962         }
19963         if (node.nodeName == "#comment") {
19964             node.parentNode.removeChild(node);
19965             // clean up silly Windows -- stuff?
19966             return; 
19967         }
19968         
19969         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19970             node.parentNode.removeChild(node);
19971             return;
19972         }
19973         
19974         // remove - but keep children..
19975         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19976             while (node.childNodes.length) {
19977                 var cn = node.childNodes[0];
19978                 node.removeChild(cn);
19979                 node.parentNode.insertBefore(cn, node);
19980             }
19981             node.parentNode.removeChild(node);
19982             this.iterateChildren(node, this.cleanWord);
19983             return;
19984         }
19985         // clean styles
19986         if (node.className.length) {
19987             
19988             var cn = node.className.split(/\W+/);
19989             var cna = [];
19990             Roo.each(cn, function(cls) {
19991                 if (cls.match(/Mso[a-zA-Z]+/)) {
19992                     return;
19993                 }
19994                 cna.push(cls);
19995             });
19996             node.className = cna.length ? cna.join(' ') : '';
19997             if (!cna.length) {
19998                 node.removeAttribute("class");
19999             }
20000         }
20001         
20002         if (node.hasAttribute("lang")) {
20003             node.removeAttribute("lang");
20004         }
20005         
20006         if (node.hasAttribute("style")) {
20007             
20008             var styles = node.getAttribute("style").split(";");
20009             var nstyle = [];
20010             Roo.each(styles, function(s) {
20011                 if (!s.match(/:/)) {
20012                     return;
20013                 }
20014                 var kv = s.split(":");
20015                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20016                     return;
20017                 }
20018                 // what ever is left... we allow.
20019                 nstyle.push(s);
20020             });
20021             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20022             if (!nstyle.length) {
20023                 node.removeAttribute('style');
20024             }
20025         }
20026         this.iterateChildren(node, this.cleanWord);
20027         
20028         
20029         
20030     },
20031     /**
20032      * iterateChildren of a Node, calling fn each time, using this as the scole..
20033      * @param {DomNode} node node to iterate children of.
20034      * @param {Function} fn method of this class to call on each item.
20035      */
20036     iterateChildren : function(node, fn)
20037     {
20038         if (!node.childNodes.length) {
20039                 return;
20040         }
20041         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20042            fn.call(this, node.childNodes[i])
20043         }
20044     },
20045     
20046     
20047     /**
20048      * cleanTableWidths.
20049      *
20050      * Quite often pasting from word etc.. results in tables with column and widths.
20051      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20052      *
20053      */
20054     cleanTableWidths : function(node)
20055     {
20056          
20057          
20058         if (!node) {
20059             this.cleanTableWidths(this.doc.body);
20060             return;
20061         }
20062         
20063         // ignore list...
20064         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20065             return; 
20066         }
20067         Roo.log(node.tagName);
20068         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20069             this.iterateChildren(node, this.cleanTableWidths);
20070             return;
20071         }
20072         if (node.hasAttribute('width')) {
20073             node.removeAttribute('width');
20074         }
20075         
20076          
20077         if (node.hasAttribute("style")) {
20078             // pretty basic...
20079             
20080             var styles = node.getAttribute("style").split(";");
20081             var nstyle = [];
20082             Roo.each(styles, function(s) {
20083                 if (!s.match(/:/)) {
20084                     return;
20085                 }
20086                 var kv = s.split(":");
20087                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20088                     return;
20089                 }
20090                 // what ever is left... we allow.
20091                 nstyle.push(s);
20092             });
20093             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20094             if (!nstyle.length) {
20095                 node.removeAttribute('style');
20096             }
20097         }
20098         
20099         this.iterateChildren(node, this.cleanTableWidths);
20100         
20101         
20102     },
20103     
20104     
20105     
20106     
20107     domToHTML : function(currentElement, depth, nopadtext) {
20108         
20109         depth = depth || 0;
20110         nopadtext = nopadtext || false;
20111     
20112         if (!currentElement) {
20113             return this.domToHTML(this.doc.body);
20114         }
20115         
20116         //Roo.log(currentElement);
20117         var j;
20118         var allText = false;
20119         var nodeName = currentElement.nodeName;
20120         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20121         
20122         if  (nodeName == '#text') {
20123             
20124             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20125         }
20126         
20127         
20128         var ret = '';
20129         if (nodeName != 'BODY') {
20130              
20131             var i = 0;
20132             // Prints the node tagName, such as <A>, <IMG>, etc
20133             if (tagName) {
20134                 var attr = [];
20135                 for(i = 0; i < currentElement.attributes.length;i++) {
20136                     // quoting?
20137                     var aname = currentElement.attributes.item(i).name;
20138                     if (!currentElement.attributes.item(i).value.length) {
20139                         continue;
20140                     }
20141                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20142                 }
20143                 
20144                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20145             } 
20146             else {
20147                 
20148                 // eack
20149             }
20150         } else {
20151             tagName = false;
20152         }
20153         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20154             return ret;
20155         }
20156         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20157             nopadtext = true;
20158         }
20159         
20160         
20161         // Traverse the tree
20162         i = 0;
20163         var currentElementChild = currentElement.childNodes.item(i);
20164         var allText = true;
20165         var innerHTML  = '';
20166         lastnode = '';
20167         while (currentElementChild) {
20168             // Formatting code (indent the tree so it looks nice on the screen)
20169             var nopad = nopadtext;
20170             if (lastnode == 'SPAN') {
20171                 nopad  = true;
20172             }
20173             // text
20174             if  (currentElementChild.nodeName == '#text') {
20175                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20176                 toadd = nopadtext ? toadd : toadd.trim();
20177                 if (!nopad && toadd.length > 80) {
20178                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20179                 }
20180                 innerHTML  += toadd;
20181                 
20182                 i++;
20183                 currentElementChild = currentElement.childNodes.item(i);
20184                 lastNode = '';
20185                 continue;
20186             }
20187             allText = false;
20188             
20189             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20190                 
20191             // Recursively traverse the tree structure of the child node
20192             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20193             lastnode = currentElementChild.nodeName;
20194             i++;
20195             currentElementChild=currentElement.childNodes.item(i);
20196         }
20197         
20198         ret += innerHTML;
20199         
20200         if (!allText) {
20201                 // The remaining code is mostly for formatting the tree
20202             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20203         }
20204         
20205         
20206         if (tagName) {
20207             ret+= "</"+tagName+">";
20208         }
20209         return ret;
20210         
20211     },
20212         
20213     applyBlacklists : function()
20214     {
20215         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20216         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20217         
20218         this.white = [];
20219         this.black = [];
20220         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20221             if (b.indexOf(tag) > -1) {
20222                 return;
20223             }
20224             this.white.push(tag);
20225             
20226         }, this);
20227         
20228         Roo.each(w, function(tag) {
20229             if (b.indexOf(tag) > -1) {
20230                 return;
20231             }
20232             if (this.white.indexOf(tag) > -1) {
20233                 return;
20234             }
20235             this.white.push(tag);
20236             
20237         }, this);
20238         
20239         
20240         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20241             if (w.indexOf(tag) > -1) {
20242                 return;
20243             }
20244             this.black.push(tag);
20245             
20246         }, this);
20247         
20248         Roo.each(b, function(tag) {
20249             if (w.indexOf(tag) > -1) {
20250                 return;
20251             }
20252             if (this.black.indexOf(tag) > -1) {
20253                 return;
20254             }
20255             this.black.push(tag);
20256             
20257         }, this);
20258         
20259         
20260         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20261         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20262         
20263         this.cwhite = [];
20264         this.cblack = [];
20265         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20266             if (b.indexOf(tag) > -1) {
20267                 return;
20268             }
20269             this.cwhite.push(tag);
20270             
20271         }, this);
20272         
20273         Roo.each(w, function(tag) {
20274             if (b.indexOf(tag) > -1) {
20275                 return;
20276             }
20277             if (this.cwhite.indexOf(tag) > -1) {
20278                 return;
20279             }
20280             this.cwhite.push(tag);
20281             
20282         }, this);
20283         
20284         
20285         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20286             if (w.indexOf(tag) > -1) {
20287                 return;
20288             }
20289             this.cblack.push(tag);
20290             
20291         }, this);
20292         
20293         Roo.each(b, function(tag) {
20294             if (w.indexOf(tag) > -1) {
20295                 return;
20296             }
20297             if (this.cblack.indexOf(tag) > -1) {
20298                 return;
20299             }
20300             this.cblack.push(tag);
20301             
20302         }, this);
20303     },
20304     
20305     setStylesheets : function(stylesheets)
20306     {
20307         if(typeof(stylesheets) == 'string'){
20308             Roo.get(this.iframe.contentDocument.head).createChild({
20309                 tag : 'link',
20310                 rel : 'stylesheet',
20311                 type : 'text/css',
20312                 href : stylesheets
20313             });
20314             
20315             return;
20316         }
20317         var _this = this;
20318      
20319         Roo.each(stylesheets, function(s) {
20320             if(!s.length){
20321                 return;
20322             }
20323             
20324             Roo.get(_this.iframe.contentDocument.head).createChild({
20325                 tag : 'link',
20326                 rel : 'stylesheet',
20327                 type : 'text/css',
20328                 href : s
20329             });
20330         });
20331
20332         
20333     },
20334     
20335     removeStylesheets : function()
20336     {
20337         var _this = this;
20338         
20339         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20340             s.remove();
20341         });
20342     }
20343     
20344     // hide stuff that is not compatible
20345     /**
20346      * @event blur
20347      * @hide
20348      */
20349     /**
20350      * @event change
20351      * @hide
20352      */
20353     /**
20354      * @event focus
20355      * @hide
20356      */
20357     /**
20358      * @event specialkey
20359      * @hide
20360      */
20361     /**
20362      * @cfg {String} fieldClass @hide
20363      */
20364     /**
20365      * @cfg {String} focusClass @hide
20366      */
20367     /**
20368      * @cfg {String} autoCreate @hide
20369      */
20370     /**
20371      * @cfg {String} inputType @hide
20372      */
20373     /**
20374      * @cfg {String} invalidClass @hide
20375      */
20376     /**
20377      * @cfg {String} invalidText @hide
20378      */
20379     /**
20380      * @cfg {String} msgFx @hide
20381      */
20382     /**
20383      * @cfg {String} validateOnBlur @hide
20384      */
20385 });
20386
20387 Roo.HtmlEditorCore.white = [
20388         'area', 'br', 'img', 'input', 'hr', 'wbr',
20389         
20390        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20391        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20392        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20393        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20394        'table',   'ul',         'xmp', 
20395        
20396        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20397       'thead',   'tr', 
20398      
20399       'dir', 'menu', 'ol', 'ul', 'dl',
20400        
20401       'embed',  'object'
20402 ];
20403
20404
20405 Roo.HtmlEditorCore.black = [
20406     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20407         'applet', // 
20408         'base',   'basefont', 'bgsound', 'blink',  'body', 
20409         'frame',  'frameset', 'head',    'html',   'ilayer', 
20410         'iframe', 'layer',  'link',     'meta',    'object',   
20411         'script', 'style' ,'title',  'xml' // clean later..
20412 ];
20413 Roo.HtmlEditorCore.clean = [
20414     'script', 'style', 'title', 'xml'
20415 ];
20416 Roo.HtmlEditorCore.remove = [
20417     'font'
20418 ];
20419 // attributes..
20420
20421 Roo.HtmlEditorCore.ablack = [
20422     'on'
20423 ];
20424     
20425 Roo.HtmlEditorCore.aclean = [ 
20426     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20427 ];
20428
20429 // protocols..
20430 Roo.HtmlEditorCore.pwhite= [
20431         'http',  'https',  'mailto'
20432 ];
20433
20434 // white listed style attributes.
20435 Roo.HtmlEditorCore.cwhite= [
20436       //  'text-align', /// default is to allow most things..
20437       
20438          
20439 //        'font-size'//??
20440 ];
20441
20442 // black listed style attributes.
20443 Roo.HtmlEditorCore.cblack= [
20444       //  'font-size' -- this can be set by the project 
20445 ];
20446
20447
20448 Roo.HtmlEditorCore.swapCodes   =[ 
20449     [    8211, "--" ], 
20450     [    8212, "--" ], 
20451     [    8216,  "'" ],  
20452     [    8217, "'" ],  
20453     [    8220, '"' ],  
20454     [    8221, '"' ],  
20455     [    8226, "*" ],  
20456     [    8230, "..." ]
20457 ]; 
20458
20459     /*
20460  * - LGPL
20461  *
20462  * HtmlEditor
20463  * 
20464  */
20465
20466 /**
20467  * @class Roo.bootstrap.HtmlEditor
20468  * @extends Roo.bootstrap.TextArea
20469  * Bootstrap HtmlEditor class
20470
20471  * @constructor
20472  * Create a new HtmlEditor
20473  * @param {Object} config The config object
20474  */
20475
20476 Roo.bootstrap.HtmlEditor = function(config){
20477     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20478     if (!this.toolbars) {
20479         this.toolbars = [];
20480     }
20481     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20482     this.addEvents({
20483             /**
20484              * @event initialize
20485              * Fires when the editor is fully initialized (including the iframe)
20486              * @param {HtmlEditor} this
20487              */
20488             initialize: true,
20489             /**
20490              * @event activate
20491              * Fires when the editor is first receives the focus. Any insertion must wait
20492              * until after this event.
20493              * @param {HtmlEditor} this
20494              */
20495             activate: true,
20496              /**
20497              * @event beforesync
20498              * Fires before the textarea is updated with content from the editor iframe. Return false
20499              * to cancel the sync.
20500              * @param {HtmlEditor} this
20501              * @param {String} html
20502              */
20503             beforesync: true,
20504              /**
20505              * @event beforepush
20506              * Fires before the iframe editor is updated with content from the textarea. Return false
20507              * to cancel the push.
20508              * @param {HtmlEditor} this
20509              * @param {String} html
20510              */
20511             beforepush: true,
20512              /**
20513              * @event sync
20514              * Fires when the textarea is updated with content from the editor iframe.
20515              * @param {HtmlEditor} this
20516              * @param {String} html
20517              */
20518             sync: true,
20519              /**
20520              * @event push
20521              * Fires when the iframe editor is updated with content from the textarea.
20522              * @param {HtmlEditor} this
20523              * @param {String} html
20524              */
20525             push: true,
20526              /**
20527              * @event editmodechange
20528              * Fires when the editor switches edit modes
20529              * @param {HtmlEditor} this
20530              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20531              */
20532             editmodechange: true,
20533             /**
20534              * @event editorevent
20535              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20536              * @param {HtmlEditor} this
20537              */
20538             editorevent: true,
20539             /**
20540              * @event firstfocus
20541              * Fires when on first focus - needed by toolbars..
20542              * @param {HtmlEditor} this
20543              */
20544             firstfocus: true,
20545             /**
20546              * @event autosave
20547              * Auto save the htmlEditor value as a file into Events
20548              * @param {HtmlEditor} this
20549              */
20550             autosave: true,
20551             /**
20552              * @event savedpreview
20553              * preview the saved version of htmlEditor
20554              * @param {HtmlEditor} this
20555              */
20556             savedpreview: true
20557         });
20558 };
20559
20560
20561 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20562     
20563     
20564       /**
20565      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20566      */
20567     toolbars : false,
20568    
20569      /**
20570      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20571      *                        Roo.resizable.
20572      */
20573     resizable : false,
20574      /**
20575      * @cfg {Number} height (in pixels)
20576      */   
20577     height: 300,
20578    /**
20579      * @cfg {Number} width (in pixels)
20580      */   
20581     width: false,
20582     
20583     /**
20584      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20585      * 
20586      */
20587     stylesheets: false,
20588     
20589     // id of frame..
20590     frameId: false,
20591     
20592     // private properties
20593     validationEvent : false,
20594     deferHeight: true,
20595     initialized : false,
20596     activated : false,
20597     
20598     onFocus : Roo.emptyFn,
20599     iframePad:3,
20600     hideMode:'offsets',
20601     
20602     
20603     tbContainer : false,
20604     
20605     toolbarContainer :function() {
20606         return this.wrap.select('.x-html-editor-tb',true).first();
20607     },
20608
20609     /**
20610      * Protected method that will not generally be called directly. It
20611      * is called when the editor creates its toolbar. Override this method if you need to
20612      * add custom toolbar buttons.
20613      * @param {HtmlEditor} editor
20614      */
20615     createToolbar : function(){
20616         
20617         Roo.log("create toolbars");
20618         
20619         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20620         this.toolbars[0].render(this.toolbarContainer());
20621         
20622         return;
20623         
20624 //        if (!editor.toolbars || !editor.toolbars.length) {
20625 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20626 //        }
20627 //        
20628 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20629 //            editor.toolbars[i] = Roo.factory(
20630 //                    typeof(editor.toolbars[i]) == 'string' ?
20631 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20632 //                Roo.bootstrap.HtmlEditor);
20633 //            editor.toolbars[i].init(editor);
20634 //        }
20635     },
20636
20637      
20638     // private
20639     onRender : function(ct, position)
20640     {
20641        // Roo.log("Call onRender: " + this.xtype);
20642         var _t = this;
20643         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20644       
20645         this.wrap = this.inputEl().wrap({
20646             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20647         });
20648         
20649         this.editorcore.onRender(ct, position);
20650          
20651         if (this.resizable) {
20652             this.resizeEl = new Roo.Resizable(this.wrap, {
20653                 pinned : true,
20654                 wrap: true,
20655                 dynamic : true,
20656                 minHeight : this.height,
20657                 height: this.height,
20658                 handles : this.resizable,
20659                 width: this.width,
20660                 listeners : {
20661                     resize : function(r, w, h) {
20662                         _t.onResize(w,h); // -something
20663                     }
20664                 }
20665             });
20666             
20667         }
20668         this.createToolbar(this);
20669        
20670         
20671         if(!this.width && this.resizable){
20672             this.setSize(this.wrap.getSize());
20673         }
20674         if (this.resizeEl) {
20675             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20676             // should trigger onReize..
20677         }
20678         
20679     },
20680
20681     // private
20682     onResize : function(w, h)
20683     {
20684         Roo.log('resize: ' +w + ',' + h );
20685         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20686         var ew = false;
20687         var eh = false;
20688         
20689         if(this.inputEl() ){
20690             if(typeof w == 'number'){
20691                 var aw = w - this.wrap.getFrameWidth('lr');
20692                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20693                 ew = aw;
20694             }
20695             if(typeof h == 'number'){
20696                  var tbh = -11;  // fixme it needs to tool bar size!
20697                 for (var i =0; i < this.toolbars.length;i++) {
20698                     // fixme - ask toolbars for heights?
20699                     tbh += this.toolbars[i].el.getHeight();
20700                     //if (this.toolbars[i].footer) {
20701                     //    tbh += this.toolbars[i].footer.el.getHeight();
20702                     //}
20703                 }
20704               
20705                 
20706                 
20707                 
20708                 
20709                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20710                 ah -= 5; // knock a few pixes off for look..
20711                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20712                 var eh = ah;
20713             }
20714         }
20715         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20716         this.editorcore.onResize(ew,eh);
20717         
20718     },
20719
20720     /**
20721      * Toggles the editor between standard and source edit mode.
20722      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20723      */
20724     toggleSourceEdit : function(sourceEditMode)
20725     {
20726         this.editorcore.toggleSourceEdit(sourceEditMode);
20727         
20728         if(this.editorcore.sourceEditMode){
20729             Roo.log('editor - showing textarea');
20730             
20731 //            Roo.log('in');
20732 //            Roo.log(this.syncValue());
20733             this.syncValue();
20734             this.inputEl().removeClass(['hide', 'x-hidden']);
20735             this.inputEl().dom.removeAttribute('tabIndex');
20736             this.inputEl().focus();
20737         }else{
20738             Roo.log('editor - hiding textarea');
20739 //            Roo.log('out')
20740 //            Roo.log(this.pushValue()); 
20741             this.pushValue();
20742             
20743             this.inputEl().addClass(['hide', 'x-hidden']);
20744             this.inputEl().dom.setAttribute('tabIndex', -1);
20745             //this.deferFocus();
20746         }
20747          
20748         if(this.resizable){
20749             this.setSize(this.wrap.getSize());
20750         }
20751         
20752         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20753     },
20754  
20755     // private (for BoxComponent)
20756     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20757
20758     // private (for BoxComponent)
20759     getResizeEl : function(){
20760         return this.wrap;
20761     },
20762
20763     // private (for BoxComponent)
20764     getPositionEl : function(){
20765         return this.wrap;
20766     },
20767
20768     // private
20769     initEvents : function(){
20770         this.originalValue = this.getValue();
20771     },
20772
20773 //    /**
20774 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20775 //     * @method
20776 //     */
20777 //    markInvalid : Roo.emptyFn,
20778 //    /**
20779 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20780 //     * @method
20781 //     */
20782 //    clearInvalid : Roo.emptyFn,
20783
20784     setValue : function(v){
20785         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20786         this.editorcore.pushValue();
20787     },
20788
20789      
20790     // private
20791     deferFocus : function(){
20792         this.focus.defer(10, this);
20793     },
20794
20795     // doc'ed in Field
20796     focus : function(){
20797         this.editorcore.focus();
20798         
20799     },
20800       
20801
20802     // private
20803     onDestroy : function(){
20804         
20805         
20806         
20807         if(this.rendered){
20808             
20809             for (var i =0; i < this.toolbars.length;i++) {
20810                 // fixme - ask toolbars for heights?
20811                 this.toolbars[i].onDestroy();
20812             }
20813             
20814             this.wrap.dom.innerHTML = '';
20815             this.wrap.remove();
20816         }
20817     },
20818
20819     // private
20820     onFirstFocus : function(){
20821         //Roo.log("onFirstFocus");
20822         this.editorcore.onFirstFocus();
20823          for (var i =0; i < this.toolbars.length;i++) {
20824             this.toolbars[i].onFirstFocus();
20825         }
20826         
20827     },
20828     
20829     // private
20830     syncValue : function()
20831     {   
20832         this.editorcore.syncValue();
20833     },
20834     
20835     pushValue : function()
20836     {   
20837         this.editorcore.pushValue();
20838     }
20839      
20840     
20841     // hide stuff that is not compatible
20842     /**
20843      * @event blur
20844      * @hide
20845      */
20846     /**
20847      * @event change
20848      * @hide
20849      */
20850     /**
20851      * @event focus
20852      * @hide
20853      */
20854     /**
20855      * @event specialkey
20856      * @hide
20857      */
20858     /**
20859      * @cfg {String} fieldClass @hide
20860      */
20861     /**
20862      * @cfg {String} focusClass @hide
20863      */
20864     /**
20865      * @cfg {String} autoCreate @hide
20866      */
20867     /**
20868      * @cfg {String} inputType @hide
20869      */
20870     /**
20871      * @cfg {String} invalidClass @hide
20872      */
20873     /**
20874      * @cfg {String} invalidText @hide
20875      */
20876     /**
20877      * @cfg {String} msgFx @hide
20878      */
20879     /**
20880      * @cfg {String} validateOnBlur @hide
20881      */
20882 });
20883  
20884     
20885    
20886    
20887    
20888       
20889 Roo.namespace('Roo.bootstrap.htmleditor');
20890 /**
20891  * @class Roo.bootstrap.HtmlEditorToolbar1
20892  * Basic Toolbar
20893  * 
20894  * Usage:
20895  *
20896  new Roo.bootstrap.HtmlEditor({
20897     ....
20898     toolbars : [
20899         new Roo.bootstrap.HtmlEditorToolbar1({
20900             disable : { fonts: 1 , format: 1, ..., ... , ...],
20901             btns : [ .... ]
20902         })
20903     }
20904      
20905  * 
20906  * @cfg {Object} disable List of elements to disable..
20907  * @cfg {Array} btns List of additional buttons.
20908  * 
20909  * 
20910  * NEEDS Extra CSS? 
20911  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20912  */
20913  
20914 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20915 {
20916     
20917     Roo.apply(this, config);
20918     
20919     // default disabled, based on 'good practice'..
20920     this.disable = this.disable || {};
20921     Roo.applyIf(this.disable, {
20922         fontSize : true,
20923         colors : true,
20924         specialElements : true
20925     });
20926     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20927     
20928     this.editor = config.editor;
20929     this.editorcore = config.editor.editorcore;
20930     
20931     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20932     
20933     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20934     // dont call parent... till later.
20935 }
20936 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20937      
20938     bar : true,
20939     
20940     editor : false,
20941     editorcore : false,
20942     
20943     
20944     formats : [
20945         "p" ,  
20946         "h1","h2","h3","h4","h5","h6", 
20947         "pre", "code", 
20948         "abbr", "acronym", "address", "cite", "samp", "var",
20949         'div','span'
20950     ],
20951     
20952     onRender : function(ct, position)
20953     {
20954        // Roo.log("Call onRender: " + this.xtype);
20955         
20956        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20957        Roo.log(this.el);
20958        this.el.dom.style.marginBottom = '0';
20959        var _this = this;
20960        var editorcore = this.editorcore;
20961        var editor= this.editor;
20962        
20963        var children = [];
20964        var btn = function(id,cmd , toggle, handler){
20965        
20966             var  event = toggle ? 'toggle' : 'click';
20967        
20968             var a = {
20969                 size : 'sm',
20970                 xtype: 'Button',
20971                 xns: Roo.bootstrap,
20972                 glyphicon : id,
20973                 cmd : id || cmd,
20974                 enableToggle:toggle !== false,
20975                 //html : 'submit'
20976                 pressed : toggle ? false : null,
20977                 listeners : {}
20978             };
20979             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20980                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20981             };
20982             children.push(a);
20983             return a;
20984        }
20985         
20986         var style = {
20987                 xtype: 'Button',
20988                 size : 'sm',
20989                 xns: Roo.bootstrap,
20990                 glyphicon : 'font',
20991                 //html : 'submit'
20992                 menu : {
20993                     xtype: 'Menu',
20994                     xns: Roo.bootstrap,
20995                     items:  []
20996                 }
20997         };
20998         Roo.each(this.formats, function(f) {
20999             style.menu.items.push({
21000                 xtype :'MenuItem',
21001                 xns: Roo.bootstrap,
21002                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21003                 tagname : f,
21004                 listeners : {
21005                     click : function()
21006                     {
21007                         editorcore.insertTag(this.tagname);
21008                         editor.focus();
21009                     }
21010                 }
21011                 
21012             });
21013         });
21014          children.push(style);   
21015             
21016             
21017         btn('bold',false,true);
21018         btn('italic',false,true);
21019         btn('align-left', 'justifyleft',true);
21020         btn('align-center', 'justifycenter',true);
21021         btn('align-right' , 'justifyright',true);
21022         btn('link', false, false, function(btn) {
21023             //Roo.log("create link?");
21024             var url = prompt(this.createLinkText, this.defaultLinkValue);
21025             if(url && url != 'http:/'+'/'){
21026                 this.editorcore.relayCmd('createlink', url);
21027             }
21028         }),
21029         btn('list','insertunorderedlist',true);
21030         btn('pencil', false,true, function(btn){
21031                 Roo.log(this);
21032                 
21033                 this.toggleSourceEdit(btn.pressed);
21034         });
21035         /*
21036         var cog = {
21037                 xtype: 'Button',
21038                 size : 'sm',
21039                 xns: Roo.bootstrap,
21040                 glyphicon : 'cog',
21041                 //html : 'submit'
21042                 menu : {
21043                     xtype: 'Menu',
21044                     xns: Roo.bootstrap,
21045                     items:  []
21046                 }
21047         };
21048         
21049         cog.menu.items.push({
21050             xtype :'MenuItem',
21051             xns: Roo.bootstrap,
21052             html : Clean styles,
21053             tagname : f,
21054             listeners : {
21055                 click : function()
21056                 {
21057                     editorcore.insertTag(this.tagname);
21058                     editor.focus();
21059                 }
21060             }
21061             
21062         });
21063        */
21064         
21065          
21066        this.xtype = 'NavSimplebar';
21067         
21068         for(var i=0;i< children.length;i++) {
21069             
21070             this.buttons.add(this.addxtypeChild(children[i]));
21071             
21072         }
21073         
21074         editor.on('editorevent', this.updateToolbar, this);
21075     },
21076     onBtnClick : function(id)
21077     {
21078        this.editorcore.relayCmd(id);
21079        this.editorcore.focus();
21080     },
21081     
21082     /**
21083      * Protected method that will not generally be called directly. It triggers
21084      * a toolbar update by reading the markup state of the current selection in the editor.
21085      */
21086     updateToolbar: function(){
21087
21088         if(!this.editorcore.activated){
21089             this.editor.onFirstFocus(); // is this neeed?
21090             return;
21091         }
21092
21093         var btns = this.buttons; 
21094         var doc = this.editorcore.doc;
21095         btns.get('bold').setActive(doc.queryCommandState('bold'));
21096         btns.get('italic').setActive(doc.queryCommandState('italic'));
21097         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21098         
21099         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21100         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21101         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21102         
21103         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21104         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21105          /*
21106         
21107         var ans = this.editorcore.getAllAncestors();
21108         if (this.formatCombo) {
21109             
21110             
21111             var store = this.formatCombo.store;
21112             this.formatCombo.setValue("");
21113             for (var i =0; i < ans.length;i++) {
21114                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21115                     // select it..
21116                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21117                     break;
21118                 }
21119             }
21120         }
21121         
21122         
21123         
21124         // hides menus... - so this cant be on a menu...
21125         Roo.bootstrap.MenuMgr.hideAll();
21126         */
21127         Roo.bootstrap.MenuMgr.hideAll();
21128         //this.editorsyncValue();
21129     },
21130     onFirstFocus: function() {
21131         this.buttons.each(function(item){
21132            item.enable();
21133         });
21134     },
21135     toggleSourceEdit : function(sourceEditMode){
21136         
21137           
21138         if(sourceEditMode){
21139             Roo.log("disabling buttons");
21140            this.buttons.each( function(item){
21141                 if(item.cmd != 'pencil'){
21142                     item.disable();
21143                 }
21144             });
21145           
21146         }else{
21147             Roo.log("enabling buttons");
21148             if(this.editorcore.initialized){
21149                 this.buttons.each( function(item){
21150                     item.enable();
21151                 });
21152             }
21153             
21154         }
21155         Roo.log("calling toggole on editor");
21156         // tell the editor that it's been pressed..
21157         this.editor.toggleSourceEdit(sourceEditMode);
21158        
21159     }
21160 });
21161
21162
21163
21164
21165
21166 /**
21167  * @class Roo.bootstrap.Table.AbstractSelectionModel
21168  * @extends Roo.util.Observable
21169  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21170  * implemented by descendant classes.  This class should not be directly instantiated.
21171  * @constructor
21172  */
21173 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21174     this.locked = false;
21175     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21176 };
21177
21178
21179 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21180     /** @ignore Called by the grid automatically. Do not call directly. */
21181     init : function(grid){
21182         this.grid = grid;
21183         this.initEvents();
21184     },
21185
21186     /**
21187      * Locks the selections.
21188      */
21189     lock : function(){
21190         this.locked = true;
21191     },
21192
21193     /**
21194      * Unlocks the selections.
21195      */
21196     unlock : function(){
21197         this.locked = false;
21198     },
21199
21200     /**
21201      * Returns true if the selections are locked.
21202      * @return {Boolean}
21203      */
21204     isLocked : function(){
21205         return this.locked;
21206     }
21207 });
21208 /**
21209  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21210  * @class Roo.bootstrap.Table.RowSelectionModel
21211  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21212  * It supports multiple selections and keyboard selection/navigation. 
21213  * @constructor
21214  * @param {Object} config
21215  */
21216
21217 Roo.bootstrap.Table.RowSelectionModel = function(config){
21218     Roo.apply(this, config);
21219     this.selections = new Roo.util.MixedCollection(false, function(o){
21220         return o.id;
21221     });
21222
21223     this.last = false;
21224     this.lastActive = false;
21225
21226     this.addEvents({
21227         /**
21228              * @event selectionchange
21229              * Fires when the selection changes
21230              * @param {SelectionModel} this
21231              */
21232             "selectionchange" : true,
21233         /**
21234              * @event afterselectionchange
21235              * Fires after the selection changes (eg. by key press or clicking)
21236              * @param {SelectionModel} this
21237              */
21238             "afterselectionchange" : true,
21239         /**
21240              * @event beforerowselect
21241              * Fires when a row is selected being selected, return false to cancel.
21242              * @param {SelectionModel} this
21243              * @param {Number} rowIndex The selected index
21244              * @param {Boolean} keepExisting False if other selections will be cleared
21245              */
21246             "beforerowselect" : true,
21247         /**
21248              * @event rowselect
21249              * Fires when a row is selected.
21250              * @param {SelectionModel} this
21251              * @param {Number} rowIndex The selected index
21252              * @param {Roo.data.Record} r The record
21253              */
21254             "rowselect" : true,
21255         /**
21256              * @event rowdeselect
21257              * Fires when a row is deselected.
21258              * @param {SelectionModel} this
21259              * @param {Number} rowIndex The selected index
21260              */
21261         "rowdeselect" : true
21262     });
21263     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21264     this.locked = false;
21265 };
21266
21267 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21268     /**
21269      * @cfg {Boolean} singleSelect
21270      * True to allow selection of only one row at a time (defaults to false)
21271      */
21272     singleSelect : false,
21273
21274     // private
21275     initEvents : function(){
21276
21277         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21278             this.grid.on("mousedown", this.handleMouseDown, this);
21279         }else{ // allow click to work like normal
21280             this.grid.on("rowclick", this.handleDragableRowClick, this);
21281         }
21282
21283         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21284             "up" : function(e){
21285                 if(!e.shiftKey){
21286                     this.selectPrevious(e.shiftKey);
21287                 }else if(this.last !== false && this.lastActive !== false){
21288                     var last = this.last;
21289                     this.selectRange(this.last,  this.lastActive-1);
21290                     this.grid.getView().focusRow(this.lastActive);
21291                     if(last !== false){
21292                         this.last = last;
21293                     }
21294                 }else{
21295                     this.selectFirstRow();
21296                 }
21297                 this.fireEvent("afterselectionchange", this);
21298             },
21299             "down" : function(e){
21300                 if(!e.shiftKey){
21301                     this.selectNext(e.shiftKey);
21302                 }else if(this.last !== false && this.lastActive !== false){
21303                     var last = this.last;
21304                     this.selectRange(this.last,  this.lastActive+1);
21305                     this.grid.getView().focusRow(this.lastActive);
21306                     if(last !== false){
21307                         this.last = last;
21308                     }
21309                 }else{
21310                     this.selectFirstRow();
21311                 }
21312                 this.fireEvent("afterselectionchange", this);
21313             },
21314             scope: this
21315         });
21316
21317         var view = this.grid.view;
21318         view.on("refresh", this.onRefresh, this);
21319         view.on("rowupdated", this.onRowUpdated, this);
21320         view.on("rowremoved", this.onRemove, this);
21321     },
21322
21323     // private
21324     onRefresh : function(){
21325         var ds = this.grid.dataSource, i, v = this.grid.view;
21326         var s = this.selections;
21327         s.each(function(r){
21328             if((i = ds.indexOfId(r.id)) != -1){
21329                 v.onRowSelect(i);
21330             }else{
21331                 s.remove(r);
21332             }
21333         });
21334     },
21335
21336     // private
21337     onRemove : function(v, index, r){
21338         this.selections.remove(r);
21339     },
21340
21341     // private
21342     onRowUpdated : function(v, index, r){
21343         if(this.isSelected(r)){
21344             v.onRowSelect(index);
21345         }
21346     },
21347
21348     /**
21349      * Select records.
21350      * @param {Array} records The records to select
21351      * @param {Boolean} keepExisting (optional) True to keep existing selections
21352      */
21353     selectRecords : function(records, keepExisting){
21354         if(!keepExisting){
21355             this.clearSelections();
21356         }
21357         var ds = this.grid.dataSource;
21358         for(var i = 0, len = records.length; i < len; i++){
21359             this.selectRow(ds.indexOf(records[i]), true);
21360         }
21361     },
21362
21363     /**
21364      * Gets the number of selected rows.
21365      * @return {Number}
21366      */
21367     getCount : function(){
21368         return this.selections.length;
21369     },
21370
21371     /**
21372      * Selects the first row in the grid.
21373      */
21374     selectFirstRow : function(){
21375         this.selectRow(0);
21376     },
21377
21378     /**
21379      * Select the last row.
21380      * @param {Boolean} keepExisting (optional) True to keep existing selections
21381      */
21382     selectLastRow : function(keepExisting){
21383         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21384     },
21385
21386     /**
21387      * Selects the row immediately following the last selected row.
21388      * @param {Boolean} keepExisting (optional) True to keep existing selections
21389      */
21390     selectNext : function(keepExisting){
21391         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21392             this.selectRow(this.last+1, keepExisting);
21393             this.grid.getView().focusRow(this.last);
21394         }
21395     },
21396
21397     /**
21398      * Selects the row that precedes the last selected row.
21399      * @param {Boolean} keepExisting (optional) True to keep existing selections
21400      */
21401     selectPrevious : function(keepExisting){
21402         if(this.last){
21403             this.selectRow(this.last-1, keepExisting);
21404             this.grid.getView().focusRow(this.last);
21405         }
21406     },
21407
21408     /**
21409      * Returns the selected records
21410      * @return {Array} Array of selected records
21411      */
21412     getSelections : function(){
21413         return [].concat(this.selections.items);
21414     },
21415
21416     /**
21417      * Returns the first selected record.
21418      * @return {Record}
21419      */
21420     getSelected : function(){
21421         return this.selections.itemAt(0);
21422     },
21423
21424
21425     /**
21426      * Clears all selections.
21427      */
21428     clearSelections : function(fast){
21429         if(this.locked) return;
21430         if(fast !== true){
21431             var ds = this.grid.dataSource;
21432             var s = this.selections;
21433             s.each(function(r){
21434                 this.deselectRow(ds.indexOfId(r.id));
21435             }, this);
21436             s.clear();
21437         }else{
21438             this.selections.clear();
21439         }
21440         this.last = false;
21441     },
21442
21443
21444     /**
21445      * Selects all rows.
21446      */
21447     selectAll : function(){
21448         if(this.locked) return;
21449         this.selections.clear();
21450         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21451             this.selectRow(i, true);
21452         }
21453     },
21454
21455     /**
21456      * Returns True if there is a selection.
21457      * @return {Boolean}
21458      */
21459     hasSelection : function(){
21460         return this.selections.length > 0;
21461     },
21462
21463     /**
21464      * Returns True if the specified row is selected.
21465      * @param {Number/Record} record The record or index of the record to check
21466      * @return {Boolean}
21467      */
21468     isSelected : function(index){
21469         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21470         return (r && this.selections.key(r.id) ? true : false);
21471     },
21472
21473     /**
21474      * Returns True if the specified record id is selected.
21475      * @param {String} id The id of record to check
21476      * @return {Boolean}
21477      */
21478     isIdSelected : function(id){
21479         return (this.selections.key(id) ? true : false);
21480     },
21481
21482     // private
21483     handleMouseDown : function(e, t){
21484         var view = this.grid.getView(), rowIndex;
21485         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21486             return;
21487         };
21488         if(e.shiftKey && this.last !== false){
21489             var last = this.last;
21490             this.selectRange(last, rowIndex, e.ctrlKey);
21491             this.last = last; // reset the last
21492             view.focusRow(rowIndex);
21493         }else{
21494             var isSelected = this.isSelected(rowIndex);
21495             if(e.button !== 0 && isSelected){
21496                 view.focusRow(rowIndex);
21497             }else if(e.ctrlKey && isSelected){
21498                 this.deselectRow(rowIndex);
21499             }else if(!isSelected){
21500                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21501                 view.focusRow(rowIndex);
21502             }
21503         }
21504         this.fireEvent("afterselectionchange", this);
21505     },
21506     // private
21507     handleDragableRowClick :  function(grid, rowIndex, e) 
21508     {
21509         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21510             this.selectRow(rowIndex, false);
21511             grid.view.focusRow(rowIndex);
21512              this.fireEvent("afterselectionchange", this);
21513         }
21514     },
21515     
21516     /**
21517      * Selects multiple rows.
21518      * @param {Array} rows Array of the indexes of the row to select
21519      * @param {Boolean} keepExisting (optional) True to keep existing selections
21520      */
21521     selectRows : function(rows, keepExisting){
21522         if(!keepExisting){
21523             this.clearSelections();
21524         }
21525         for(var i = 0, len = rows.length; i < len; i++){
21526             this.selectRow(rows[i], true);
21527         }
21528     },
21529
21530     /**
21531      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21532      * @param {Number} startRow The index of the first row in the range
21533      * @param {Number} endRow The index of the last row in the range
21534      * @param {Boolean} keepExisting (optional) True to retain existing selections
21535      */
21536     selectRange : function(startRow, endRow, keepExisting){
21537         if(this.locked) return;
21538         if(!keepExisting){
21539             this.clearSelections();
21540         }
21541         if(startRow <= endRow){
21542             for(var i = startRow; i <= endRow; i++){
21543                 this.selectRow(i, true);
21544             }
21545         }else{
21546             for(var i = startRow; i >= endRow; i--){
21547                 this.selectRow(i, true);
21548             }
21549         }
21550     },
21551
21552     /**
21553      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21554      * @param {Number} startRow The index of the first row in the range
21555      * @param {Number} endRow The index of the last row in the range
21556      */
21557     deselectRange : function(startRow, endRow, preventViewNotify){
21558         if(this.locked) return;
21559         for(var i = startRow; i <= endRow; i++){
21560             this.deselectRow(i, preventViewNotify);
21561         }
21562     },
21563
21564     /**
21565      * Selects a row.
21566      * @param {Number} row The index of the row to select
21567      * @param {Boolean} keepExisting (optional) True to keep existing selections
21568      */
21569     selectRow : function(index, keepExisting, preventViewNotify){
21570         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21571         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21572             if(!keepExisting || this.singleSelect){
21573                 this.clearSelections();
21574             }
21575             var r = this.grid.dataSource.getAt(index);
21576             this.selections.add(r);
21577             this.last = this.lastActive = index;
21578             if(!preventViewNotify){
21579                 this.grid.getView().onRowSelect(index);
21580             }
21581             this.fireEvent("rowselect", this, index, r);
21582             this.fireEvent("selectionchange", this);
21583         }
21584     },
21585
21586     /**
21587      * Deselects a row.
21588      * @param {Number} row The index of the row to deselect
21589      */
21590     deselectRow : function(index, preventViewNotify){
21591         if(this.locked) return;
21592         if(this.last == index){
21593             this.last = false;
21594         }
21595         if(this.lastActive == index){
21596             this.lastActive = false;
21597         }
21598         var r = this.grid.dataSource.getAt(index);
21599         this.selections.remove(r);
21600         if(!preventViewNotify){
21601             this.grid.getView().onRowDeselect(index);
21602         }
21603         this.fireEvent("rowdeselect", this, index);
21604         this.fireEvent("selectionchange", this);
21605     },
21606
21607     // private
21608     restoreLast : function(){
21609         if(this._last){
21610             this.last = this._last;
21611         }
21612     },
21613
21614     // private
21615     acceptsNav : function(row, col, cm){
21616         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21617     },
21618
21619     // private
21620     onEditorKey : function(field, e){
21621         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21622         if(k == e.TAB){
21623             e.stopEvent();
21624             ed.completeEdit();
21625             if(e.shiftKey){
21626                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21627             }else{
21628                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21629             }
21630         }else if(k == e.ENTER && !e.ctrlKey){
21631             e.stopEvent();
21632             ed.completeEdit();
21633             if(e.shiftKey){
21634                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21635             }else{
21636                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21637             }
21638         }else if(k == e.ESC){
21639             ed.cancelEdit();
21640         }
21641         if(newCell){
21642             g.startEditing(newCell[0], newCell[1]);
21643         }
21644     }
21645 });/*
21646  * Based on:
21647  * Ext JS Library 1.1.1
21648  * Copyright(c) 2006-2007, Ext JS, LLC.
21649  *
21650  * Originally Released Under LGPL - original licence link has changed is not relivant.
21651  *
21652  * Fork - LGPL
21653  * <script type="text/javascript">
21654  */
21655  
21656 /**
21657  * @class Roo.bootstrap.PagingToolbar
21658  * @extends Roo.bootstrap.NavSimplebar
21659  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21660  * @constructor
21661  * Create a new PagingToolbar
21662  * @param {Object} config The config object
21663  * @param {Roo.data.Store} store
21664  */
21665 Roo.bootstrap.PagingToolbar = function(config)
21666 {
21667     // old args format still supported... - xtype is prefered..
21668         // created from xtype...
21669     
21670     this.ds = config.dataSource;
21671     
21672     if (config.store && !this.ds) {
21673         this.store= Roo.factory(config.store, Roo.data);
21674         this.ds = this.store;
21675         this.ds.xmodule = this.xmodule || false;
21676     }
21677     
21678     this.toolbarItems = [];
21679     if (config.items) {
21680         this.toolbarItems = config.items;
21681     }
21682     
21683     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21684     
21685     this.cursor = 0;
21686     
21687     if (this.ds) { 
21688         this.bind(this.ds);
21689     }
21690     
21691     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21692     
21693 };
21694
21695 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21696     /**
21697      * @cfg {Roo.data.Store} dataSource
21698      * The underlying data store providing the paged data
21699      */
21700     /**
21701      * @cfg {String/HTMLElement/Element} container
21702      * container The id or element that will contain the toolbar
21703      */
21704     /**
21705      * @cfg {Boolean} displayInfo
21706      * True to display the displayMsg (defaults to false)
21707      */
21708     /**
21709      * @cfg {Number} pageSize
21710      * The number of records to display per page (defaults to 20)
21711      */
21712     pageSize: 20,
21713     /**
21714      * @cfg {String} displayMsg
21715      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21716      */
21717     displayMsg : 'Displaying {0} - {1} of {2}',
21718     /**
21719      * @cfg {String} emptyMsg
21720      * The message to display when no records are found (defaults to "No data to display")
21721      */
21722     emptyMsg : 'No data to display',
21723     /**
21724      * Customizable piece of the default paging text (defaults to "Page")
21725      * @type String
21726      */
21727     beforePageText : "Page",
21728     /**
21729      * Customizable piece of the default paging text (defaults to "of %0")
21730      * @type String
21731      */
21732     afterPageText : "of {0}",
21733     /**
21734      * Customizable piece of the default paging text (defaults to "First Page")
21735      * @type String
21736      */
21737     firstText : "First Page",
21738     /**
21739      * Customizable piece of the default paging text (defaults to "Previous Page")
21740      * @type String
21741      */
21742     prevText : "Previous Page",
21743     /**
21744      * Customizable piece of the default paging text (defaults to "Next Page")
21745      * @type String
21746      */
21747     nextText : "Next Page",
21748     /**
21749      * Customizable piece of the default paging text (defaults to "Last Page")
21750      * @type String
21751      */
21752     lastText : "Last Page",
21753     /**
21754      * Customizable piece of the default paging text (defaults to "Refresh")
21755      * @type String
21756      */
21757     refreshText : "Refresh",
21758
21759     buttons : false,
21760     // private
21761     onRender : function(ct, position) 
21762     {
21763         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21764         this.navgroup.parentId = this.id;
21765         this.navgroup.onRender(this.el, null);
21766         // add the buttons to the navgroup
21767         
21768         if(this.displayInfo){
21769             Roo.log(this.el.select('ul.navbar-nav',true).first());
21770             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21771             this.displayEl = this.el.select('.x-paging-info', true).first();
21772 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21773 //            this.displayEl = navel.el.select('span',true).first();
21774         }
21775         
21776         var _this = this;
21777         
21778         if(this.buttons){
21779             Roo.each(_this.buttons, function(e){
21780                Roo.factory(e).onRender(_this.el, null);
21781             });
21782         }
21783             
21784         Roo.each(_this.toolbarItems, function(e) {
21785             _this.navgroup.addItem(e);
21786         });
21787         
21788         
21789         this.first = this.navgroup.addItem({
21790             tooltip: this.firstText,
21791             cls: "prev",
21792             icon : 'fa fa-backward',
21793             disabled: true,
21794             preventDefault: true,
21795             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21796         });
21797         
21798         this.prev =  this.navgroup.addItem({
21799             tooltip: this.prevText,
21800             cls: "prev",
21801             icon : 'fa fa-step-backward',
21802             disabled: true,
21803             preventDefault: true,
21804             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21805         });
21806     //this.addSeparator();
21807         
21808         
21809         var field = this.navgroup.addItem( {
21810             tagtype : 'span',
21811             cls : 'x-paging-position',
21812             
21813             html : this.beforePageText  +
21814                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21815                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21816          } ); //?? escaped?
21817         
21818         this.field = field.el.select('input', true).first();
21819         this.field.on("keydown", this.onPagingKeydown, this);
21820         this.field.on("focus", function(){this.dom.select();});
21821     
21822     
21823         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21824         //this.field.setHeight(18);
21825         //this.addSeparator();
21826         this.next = this.navgroup.addItem({
21827             tooltip: this.nextText,
21828             cls: "next",
21829             html : ' <i class="fa fa-step-forward">',
21830             disabled: true,
21831             preventDefault: true,
21832             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21833         });
21834         this.last = this.navgroup.addItem({
21835             tooltip: this.lastText,
21836             icon : 'fa fa-forward',
21837             cls: "next",
21838             disabled: true,
21839             preventDefault: true,
21840             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21841         });
21842     //this.addSeparator();
21843         this.loading = this.navgroup.addItem({
21844             tooltip: this.refreshText,
21845             icon: 'fa fa-refresh',
21846             preventDefault: true,
21847             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21848         });
21849         
21850     },
21851
21852     // private
21853     updateInfo : function(){
21854         if(this.displayEl){
21855             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21856             var msg = count == 0 ?
21857                 this.emptyMsg :
21858                 String.format(
21859                     this.displayMsg,
21860                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21861                 );
21862             this.displayEl.update(msg);
21863         }
21864     },
21865
21866     // private
21867     onLoad : function(ds, r, o){
21868        this.cursor = o.params ? o.params.start : 0;
21869        var d = this.getPageData(),
21870             ap = d.activePage,
21871             ps = d.pages;
21872         
21873        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21874        this.field.dom.value = ap;
21875        this.first.setDisabled(ap == 1);
21876        this.prev.setDisabled(ap == 1);
21877        this.next.setDisabled(ap == ps);
21878        this.last.setDisabled(ap == ps);
21879        this.loading.enable();
21880        this.updateInfo();
21881     },
21882
21883     // private
21884     getPageData : function(){
21885         var total = this.ds.getTotalCount();
21886         return {
21887             total : total,
21888             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21889             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21890         };
21891     },
21892
21893     // private
21894     onLoadError : function(){
21895         this.loading.enable();
21896     },
21897
21898     // private
21899     onPagingKeydown : function(e){
21900         var k = e.getKey();
21901         var d = this.getPageData();
21902         if(k == e.RETURN){
21903             var v = this.field.dom.value, pageNum;
21904             if(!v || isNaN(pageNum = parseInt(v, 10))){
21905                 this.field.dom.value = d.activePage;
21906                 return;
21907             }
21908             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21909             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21910             e.stopEvent();
21911         }
21912         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))
21913         {
21914           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21915           this.field.dom.value = pageNum;
21916           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21917           e.stopEvent();
21918         }
21919         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21920         {
21921           var v = this.field.dom.value, pageNum; 
21922           var increment = (e.shiftKey) ? 10 : 1;
21923           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21924             increment *= -1;
21925           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21926             this.field.dom.value = d.activePage;
21927             return;
21928           }
21929           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21930           {
21931             this.field.dom.value = parseInt(v, 10) + increment;
21932             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21933             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21934           }
21935           e.stopEvent();
21936         }
21937     },
21938
21939     // private
21940     beforeLoad : function(){
21941         if(this.loading){
21942             this.loading.disable();
21943         }
21944     },
21945
21946     // private
21947     onClick : function(which){
21948         
21949         var ds = this.ds;
21950         if (!ds) {
21951             return;
21952         }
21953         
21954         switch(which){
21955             case "first":
21956                 ds.load({params:{start: 0, limit: this.pageSize}});
21957             break;
21958             case "prev":
21959                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21960             break;
21961             case "next":
21962                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21963             break;
21964             case "last":
21965                 var total = ds.getTotalCount();
21966                 var extra = total % this.pageSize;
21967                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21968                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21969             break;
21970             case "refresh":
21971                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21972             break;
21973         }
21974     },
21975
21976     /**
21977      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21978      * @param {Roo.data.Store} store The data store to unbind
21979      */
21980     unbind : function(ds){
21981         ds.un("beforeload", this.beforeLoad, this);
21982         ds.un("load", this.onLoad, this);
21983         ds.un("loadexception", this.onLoadError, this);
21984         ds.un("remove", this.updateInfo, this);
21985         ds.un("add", this.updateInfo, this);
21986         this.ds = undefined;
21987     },
21988
21989     /**
21990      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21991      * @param {Roo.data.Store} store The data store to bind
21992      */
21993     bind : function(ds){
21994         ds.on("beforeload", this.beforeLoad, this);
21995         ds.on("load", this.onLoad, this);
21996         ds.on("loadexception", this.onLoadError, this);
21997         ds.on("remove", this.updateInfo, this);
21998         ds.on("add", this.updateInfo, this);
21999         this.ds = ds;
22000     }
22001 });/*
22002  * - LGPL
22003  *
22004  * element
22005  * 
22006  */
22007
22008 /**
22009  * @class Roo.bootstrap.MessageBar
22010  * @extends Roo.bootstrap.Component
22011  * Bootstrap MessageBar class
22012  * @cfg {String} html contents of the MessageBar
22013  * @cfg {String} weight (info | success | warning | danger) default info
22014  * @cfg {String} beforeClass insert the bar before the given class
22015  * @cfg {Boolean} closable (true | false) default false
22016  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22017  * 
22018  * @constructor
22019  * Create a new Element
22020  * @param {Object} config The config object
22021  */
22022
22023 Roo.bootstrap.MessageBar = function(config){
22024     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22025 };
22026
22027 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22028     
22029     html: '',
22030     weight: 'info',
22031     closable: false,
22032     fixed: false,
22033     beforeClass: 'bootstrap-sticky-wrap',
22034     
22035     getAutoCreate : function(){
22036         
22037         var cfg = {
22038             tag: 'div',
22039             cls: 'alert alert-dismissable alert-' + this.weight,
22040             cn: [
22041                 {
22042                     tag: 'span',
22043                     cls: 'message',
22044                     html: this.html || ''
22045                 }
22046             ]
22047         }
22048         
22049         if(this.fixed){
22050             cfg.cls += ' alert-messages-fixed';
22051         }
22052         
22053         if(this.closable){
22054             cfg.cn.push({
22055                 tag: 'button',
22056                 cls: 'close',
22057                 html: 'x'
22058             });
22059         }
22060         
22061         return cfg;
22062     },
22063     
22064     onRender : function(ct, position)
22065     {
22066         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22067         
22068         if(!this.el){
22069             var cfg = Roo.apply({},  this.getAutoCreate());
22070             cfg.id = Roo.id();
22071             
22072             if (this.cls) {
22073                 cfg.cls += ' ' + this.cls;
22074             }
22075             if (this.style) {
22076                 cfg.style = this.style;
22077             }
22078             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22079             
22080             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22081         }
22082         
22083         this.el.select('>button.close').on('click', this.hide, this);
22084         
22085     },
22086     
22087     show : function()
22088     {
22089         if (!this.rendered) {
22090             this.render();
22091         }
22092         
22093         this.el.show();
22094         
22095         this.fireEvent('show', this);
22096         
22097     },
22098     
22099     hide : function()
22100     {
22101         if (!this.rendered) {
22102             this.render();
22103         }
22104         
22105         this.el.hide();
22106         
22107         this.fireEvent('hide', this);
22108     },
22109     
22110     update : function()
22111     {
22112 //        var e = this.el.dom.firstChild;
22113 //        
22114 //        if(this.closable){
22115 //            e = e.nextSibling;
22116 //        }
22117 //        
22118 //        e.data = this.html || '';
22119
22120         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22121     }
22122    
22123 });
22124
22125  
22126
22127      /*
22128  * - LGPL
22129  *
22130  * Graph
22131  * 
22132  */
22133
22134
22135 /**
22136  * @class Roo.bootstrap.Graph
22137  * @extends Roo.bootstrap.Component
22138  * Bootstrap Graph class
22139 > Prameters
22140  -sm {number} sm 4
22141  -md {number} md 5
22142  @cfg {String} graphtype  bar | vbar | pie
22143  @cfg {number} g_x coodinator | centre x (pie)
22144  @cfg {number} g_y coodinator | centre y (pie)
22145  @cfg {number} g_r radius (pie)
22146  @cfg {number} g_height height of the chart (respected by all elements in the set)
22147  @cfg {number} g_width width of the chart (respected by all elements in the set)
22148  @cfg {Object} title The title of the chart
22149     
22150  -{Array}  values
22151  -opts (object) options for the chart 
22152      o {
22153      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22154      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22155      o vgutter (number)
22156      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.
22157      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22158      o to
22159      o stretch (boolean)
22160      o }
22161  -opts (object) options for the pie
22162      o{
22163      o cut
22164      o startAngle (number)
22165      o endAngle (number)
22166      } 
22167  *
22168  * @constructor
22169  * Create a new Input
22170  * @param {Object} config The config object
22171  */
22172
22173 Roo.bootstrap.Graph = function(config){
22174     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22175     
22176     this.addEvents({
22177         // img events
22178         /**
22179          * @event click
22180          * The img click event for the img.
22181          * @param {Roo.EventObject} e
22182          */
22183         "click" : true
22184     });
22185 };
22186
22187 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22188     
22189     sm: 4,
22190     md: 5,
22191     graphtype: 'bar',
22192     g_height: 250,
22193     g_width: 400,
22194     g_x: 50,
22195     g_y: 50,
22196     g_r: 30,
22197     opts:{
22198         //g_colors: this.colors,
22199         g_type: 'soft',
22200         g_gutter: '20%'
22201
22202     },
22203     title : false,
22204
22205     getAutoCreate : function(){
22206         
22207         var cfg = {
22208             tag: 'div',
22209             html : null
22210         }
22211         
22212         
22213         return  cfg;
22214     },
22215
22216     onRender : function(ct,position){
22217         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22218         this.raphael = Raphael(this.el.dom);
22219         
22220                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22221                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22222                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22223                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22224                 /*
22225                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22226                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22227                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22228                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22229                 
22230                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22231                 r.barchart(330, 10, 300, 220, data1);
22232                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22233                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22234                 */
22235                 
22236                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22237                 // r.barchart(30, 30, 560, 250,  xdata, {
22238                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22239                 //     axis : "0 0 1 1",
22240                 //     axisxlabels :  xdata
22241                 //     //yvalues : cols,
22242                    
22243                 // });
22244 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22245 //        
22246 //        this.load(null,xdata,{
22247 //                axis : "0 0 1 1",
22248 //                axisxlabels :  xdata
22249 //                });
22250
22251     },
22252
22253     load : function(graphtype,xdata,opts){
22254         this.raphael.clear();
22255         if(!graphtype) {
22256             graphtype = this.graphtype;
22257         }
22258         if(!opts){
22259             opts = this.opts;
22260         }
22261         var r = this.raphael,
22262             fin = function () {
22263                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22264             },
22265             fout = function () {
22266                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22267             },
22268             pfin = function() {
22269                 this.sector.stop();
22270                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22271
22272                 if (this.label) {
22273                     this.label[0].stop();
22274                     this.label[0].attr({ r: 7.5 });
22275                     this.label[1].attr({ "font-weight": 800 });
22276                 }
22277             },
22278             pfout = function() {
22279                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22280
22281                 if (this.label) {
22282                     this.label[0].animate({ r: 5 }, 500, "bounce");
22283                     this.label[1].attr({ "font-weight": 400 });
22284                 }
22285             };
22286
22287         switch(graphtype){
22288             case 'bar':
22289                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22290                 break;
22291             case 'hbar':
22292                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22293                 break;
22294             case 'pie':
22295 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22296 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22297 //            
22298                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22299                 
22300                 break;
22301
22302         }
22303         
22304         if(this.title){
22305             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22306         }
22307         
22308     },
22309     
22310     setTitle: function(o)
22311     {
22312         this.title = o;
22313     },
22314     
22315     initEvents: function() {
22316         
22317         if(!this.href){
22318             this.el.on('click', this.onClick, this);
22319         }
22320     },
22321     
22322     onClick : function(e)
22323     {
22324         Roo.log('img onclick');
22325         this.fireEvent('click', this, e);
22326     }
22327    
22328 });
22329
22330  
22331 /*
22332  * - LGPL
22333  *
22334  * numberBox
22335  * 
22336  */
22337 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22338
22339 /**
22340  * @class Roo.bootstrap.dash.NumberBox
22341  * @extends Roo.bootstrap.Component
22342  * Bootstrap NumberBox class
22343  * @cfg {String} headline Box headline
22344  * @cfg {String} content Box content
22345  * @cfg {String} icon Box icon
22346  * @cfg {String} footer Footer text
22347  * @cfg {String} fhref Footer href
22348  * 
22349  * @constructor
22350  * Create a new NumberBox
22351  * @param {Object} config The config object
22352  */
22353
22354
22355 Roo.bootstrap.dash.NumberBox = function(config){
22356     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22357     
22358 };
22359
22360 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22361     
22362     headline : '',
22363     content : '',
22364     icon : '',
22365     footer : '',
22366     fhref : '',
22367     ficon : '',
22368     
22369     getAutoCreate : function(){
22370         
22371         var cfg = {
22372             tag : 'div',
22373             cls : 'small-box ',
22374             cn : [
22375                 {
22376                     tag : 'div',
22377                     cls : 'inner',
22378                     cn :[
22379                         {
22380                             tag : 'h3',
22381                             cls : 'roo-headline',
22382                             html : this.headline
22383                         },
22384                         {
22385                             tag : 'p',
22386                             cls : 'roo-content',
22387                             html : this.content
22388                         }
22389                     ]
22390                 }
22391             ]
22392         }
22393         
22394         if(this.icon){
22395             cfg.cn.push({
22396                 tag : 'div',
22397                 cls : 'icon',
22398                 cn :[
22399                     {
22400                         tag : 'i',
22401                         cls : 'ion ' + this.icon
22402                     }
22403                 ]
22404             });
22405         }
22406         
22407         if(this.footer){
22408             var footer = {
22409                 tag : 'a',
22410                 cls : 'small-box-footer',
22411                 href : this.fhref || '#',
22412                 html : this.footer
22413             };
22414             
22415             cfg.cn.push(footer);
22416             
22417         }
22418         
22419         return  cfg;
22420     },
22421
22422     onRender : function(ct,position){
22423         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22424
22425
22426        
22427                 
22428     },
22429
22430     setHeadline: function (value)
22431     {
22432         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22433     },
22434     
22435     setFooter: function (value, href)
22436     {
22437         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22438         
22439         if(href){
22440             this.el.select('a.small-box-footer',true).first().attr('href', href);
22441         }
22442         
22443     },
22444
22445     setContent: function (value)
22446     {
22447         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22448     },
22449
22450     initEvents: function() 
22451     {   
22452         
22453     }
22454     
22455 });
22456
22457  
22458 /*
22459  * - LGPL
22460  *
22461  * TabBox
22462  * 
22463  */
22464 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22465
22466 /**
22467  * @class Roo.bootstrap.dash.TabBox
22468  * @extends Roo.bootstrap.Component
22469  * Bootstrap TabBox class
22470  * @cfg {String} title Title of the TabBox
22471  * @cfg {String} icon Icon of the TabBox
22472  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22473  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22474  * 
22475  * @constructor
22476  * Create a new TabBox
22477  * @param {Object} config The config object
22478  */
22479
22480
22481 Roo.bootstrap.dash.TabBox = function(config){
22482     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22483     this.addEvents({
22484         // raw events
22485         /**
22486          * @event addpane
22487          * When a pane is added
22488          * @param {Roo.bootstrap.dash.TabPane} pane
22489          */
22490         "addpane" : true,
22491         /**
22492          * @event activatepane
22493          * When a pane is activated
22494          * @param {Roo.bootstrap.dash.TabPane} pane
22495          */
22496         "activatepane" : true
22497         
22498          
22499     });
22500     
22501     this.panes = [];
22502 };
22503
22504 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22505
22506     title : '',
22507     icon : false,
22508     showtabs : true,
22509     tabScrollable : false,
22510     
22511     getChildContainer : function()
22512     {
22513         return this.el.select('.tab-content', true).first();
22514     },
22515     
22516     getAutoCreate : function(){
22517         
22518         var header = {
22519             tag: 'li',
22520             cls: 'pull-left header',
22521             html: this.title,
22522             cn : []
22523         };
22524         
22525         if(this.icon){
22526             header.cn.push({
22527                 tag: 'i',
22528                 cls: 'fa ' + this.icon
22529             });
22530         }
22531         
22532         var h = {
22533             tag: 'ul',
22534             cls: 'nav nav-tabs pull-right',
22535             cn: [
22536                 header
22537             ]
22538         };
22539         
22540         if(this.tabScrollable){
22541             h = {
22542                 tag: 'div',
22543                 cls: 'tab-header',
22544                 cn: [
22545                     {
22546                         tag: 'ul',
22547                         cls: 'nav nav-tabs pull-right',
22548                         cn: [
22549                             header
22550                         ]
22551                     }
22552                 ]
22553             }
22554         }
22555         
22556         var cfg = {
22557             tag: 'div',
22558             cls: 'nav-tabs-custom',
22559             cn: [
22560                 h,
22561                 {
22562                     tag: 'div',
22563                     cls: 'tab-content no-padding',
22564                     cn: []
22565                 }
22566             ]
22567         }
22568
22569         return  cfg;
22570     },
22571     initEvents : function()
22572     {
22573         //Roo.log('add add pane handler');
22574         this.on('addpane', this.onAddPane, this);
22575     },
22576      /**
22577      * Updates the box title
22578      * @param {String} html to set the title to.
22579      */
22580     setTitle : function(value)
22581     {
22582         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22583     },
22584     onAddPane : function(pane)
22585     {
22586         this.panes.push(pane);
22587         //Roo.log('addpane');
22588         //Roo.log(pane);
22589         // tabs are rendere left to right..
22590         if(!this.showtabs){
22591             return;
22592         }
22593         
22594         var ctr = this.el.select('.nav-tabs', true).first();
22595          
22596          
22597         var existing = ctr.select('.nav-tab',true);
22598         var qty = existing.getCount();;
22599         
22600         
22601         var tab = ctr.createChild({
22602             tag : 'li',
22603             cls : 'nav-tab' + (qty ? '' : ' active'),
22604             cn : [
22605                 {
22606                     tag : 'a',
22607                     href:'#',
22608                     html : pane.title
22609                 }
22610             ]
22611         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22612         pane.tab = tab;
22613         
22614         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22615         if (!qty) {
22616             pane.el.addClass('active');
22617         }
22618         
22619                 
22620     },
22621     onTabClick : function(ev,un,ob,pane)
22622     {
22623         //Roo.log('tab - prev default');
22624         ev.preventDefault();
22625         
22626         
22627         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22628         pane.tab.addClass('active');
22629         //Roo.log(pane.title);
22630         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22631         // technically we should have a deactivate event.. but maybe add later.
22632         // and it should not de-activate the selected tab...
22633         this.fireEvent('activatepane', pane);
22634         pane.el.addClass('active');
22635         pane.fireEvent('activate');
22636         
22637         
22638     },
22639     
22640     getActivePane : function()
22641     {
22642         var r = false;
22643         Roo.each(this.panes, function(p) {
22644             if(p.el.hasClass('active')){
22645                 r = p;
22646                 return false;
22647             }
22648             
22649             return;
22650         });
22651         
22652         return r;
22653     }
22654     
22655     
22656 });
22657
22658  
22659 /*
22660  * - LGPL
22661  *
22662  * Tab pane
22663  * 
22664  */
22665 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22666 /**
22667  * @class Roo.bootstrap.TabPane
22668  * @extends Roo.bootstrap.Component
22669  * Bootstrap TabPane class
22670  * @cfg {Boolean} active (false | true) Default false
22671  * @cfg {String} title title of panel
22672
22673  * 
22674  * @constructor
22675  * Create a new TabPane
22676  * @param {Object} config The config object
22677  */
22678
22679 Roo.bootstrap.dash.TabPane = function(config){
22680     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22681     
22682     this.addEvents({
22683         // raw events
22684         /**
22685          * @event activate
22686          * When a pane is activated
22687          * @param {Roo.bootstrap.dash.TabPane} pane
22688          */
22689         "activate" : true
22690          
22691     });
22692 };
22693
22694 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22695     
22696     active : false,
22697     title : '',
22698     
22699     // the tabBox that this is attached to.
22700     tab : false,
22701      
22702     getAutoCreate : function() 
22703     {
22704         var cfg = {
22705             tag: 'div',
22706             cls: 'tab-pane'
22707         }
22708         
22709         if(this.active){
22710             cfg.cls += ' active';
22711         }
22712         
22713         return cfg;
22714     },
22715     initEvents  : function()
22716     {
22717         //Roo.log('trigger add pane handler');
22718         this.parent().fireEvent('addpane', this)
22719     },
22720     
22721      /**
22722      * Updates the tab title 
22723      * @param {String} html to set the title to.
22724      */
22725     setTitle: function(str)
22726     {
22727         if (!this.tab) {
22728             return;
22729         }
22730         this.title = str;
22731         this.tab.select('a', true).first().dom.innerHTML = str;
22732         
22733     }
22734     
22735     
22736     
22737 });
22738
22739  
22740
22741
22742  /*
22743  * - LGPL
22744  *
22745  * menu
22746  * 
22747  */
22748 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22749
22750 /**
22751  * @class Roo.bootstrap.menu.Menu
22752  * @extends Roo.bootstrap.Component
22753  * Bootstrap Menu class - container for Menu
22754  * @cfg {String} html Text of the menu
22755  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22756  * @cfg {String} icon Font awesome icon
22757  * @cfg {String} pos Menu align to (top | bottom) default bottom
22758  * 
22759  * 
22760  * @constructor
22761  * Create a new Menu
22762  * @param {Object} config The config object
22763  */
22764
22765
22766 Roo.bootstrap.menu.Menu = function(config){
22767     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22768     
22769     this.addEvents({
22770         /**
22771          * @event beforeshow
22772          * Fires before this menu is displayed
22773          * @param {Roo.bootstrap.menu.Menu} this
22774          */
22775         beforeshow : true,
22776         /**
22777          * @event beforehide
22778          * Fires before this menu is hidden
22779          * @param {Roo.bootstrap.menu.Menu} this
22780          */
22781         beforehide : true,
22782         /**
22783          * @event show
22784          * Fires after this menu is displayed
22785          * @param {Roo.bootstrap.menu.Menu} this
22786          */
22787         show : true,
22788         /**
22789          * @event hide
22790          * Fires after this menu is hidden
22791          * @param {Roo.bootstrap.menu.Menu} this
22792          */
22793         hide : true,
22794         /**
22795          * @event click
22796          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22797          * @param {Roo.bootstrap.menu.Menu} this
22798          * @param {Roo.EventObject} e
22799          */
22800         click : true
22801     });
22802     
22803 };
22804
22805 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22806     
22807     submenu : false,
22808     html : '',
22809     weight : 'default',
22810     icon : false,
22811     pos : 'bottom',
22812     
22813     
22814     getChildContainer : function() {
22815         if(this.isSubMenu){
22816             return this.el;
22817         }
22818         
22819         return this.el.select('ul.dropdown-menu', true).first();  
22820     },
22821     
22822     getAutoCreate : function()
22823     {
22824         var text = [
22825             {
22826                 tag : 'span',
22827                 cls : 'roo-menu-text',
22828                 html : this.html
22829             }
22830         ];
22831         
22832         if(this.icon){
22833             text.unshift({
22834                 tag : 'i',
22835                 cls : 'fa ' + this.icon
22836             })
22837         }
22838         
22839         
22840         var cfg = {
22841             tag : 'div',
22842             cls : 'btn-group',
22843             cn : [
22844                 {
22845                     tag : 'button',
22846                     cls : 'dropdown-button btn btn-' + this.weight,
22847                     cn : text
22848                 },
22849                 {
22850                     tag : 'button',
22851                     cls : 'dropdown-toggle btn btn-' + this.weight,
22852                     cn : [
22853                         {
22854                             tag : 'span',
22855                             cls : 'caret'
22856                         }
22857                     ]
22858                 },
22859                 {
22860                     tag : 'ul',
22861                     cls : 'dropdown-menu'
22862                 }
22863             ]
22864             
22865         };
22866         
22867         if(this.pos == 'top'){
22868             cfg.cls += ' dropup';
22869         }
22870         
22871         if(this.isSubMenu){
22872             cfg = {
22873                 tag : 'ul',
22874                 cls : 'dropdown-menu'
22875             }
22876         }
22877         
22878         return cfg;
22879     },
22880     
22881     onRender : function(ct, position)
22882     {
22883         this.isSubMenu = ct.hasClass('dropdown-submenu');
22884         
22885         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22886     },
22887     
22888     initEvents : function() 
22889     {
22890         if(this.isSubMenu){
22891             return;
22892         }
22893         
22894         this.hidden = true;
22895         
22896         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22897         this.triggerEl.on('click', this.onTriggerPress, this);
22898         
22899         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22900         this.buttonEl.on('click', this.onClick, this);
22901         
22902     },
22903     
22904     list : function()
22905     {
22906         if(this.isSubMenu){
22907             return this.el;
22908         }
22909         
22910         return this.el.select('ul.dropdown-menu', true).first();
22911     },
22912     
22913     onClick : function(e)
22914     {
22915         this.fireEvent("click", this, e);
22916     },
22917     
22918     onTriggerPress  : function(e)
22919     {   
22920         if (this.isVisible()) {
22921             this.hide();
22922         } else {
22923             this.show();
22924         }
22925     },
22926     
22927     isVisible : function(){
22928         return !this.hidden;
22929     },
22930     
22931     show : function()
22932     {
22933         this.fireEvent("beforeshow", this);
22934         
22935         this.hidden = false;
22936         this.el.addClass('open');
22937         
22938         Roo.get(document).on("mouseup", this.onMouseUp, this);
22939         
22940         this.fireEvent("show", this);
22941         
22942         
22943     },
22944     
22945     hide : function()
22946     {
22947         this.fireEvent("beforehide", this);
22948         
22949         this.hidden = true;
22950         this.el.removeClass('open');
22951         
22952         Roo.get(document).un("mouseup", this.onMouseUp);
22953         
22954         this.fireEvent("hide", this);
22955     },
22956     
22957     onMouseUp : function()
22958     {
22959         this.hide();
22960     }
22961     
22962 });
22963
22964  
22965  /*
22966  * - LGPL
22967  *
22968  * menu item
22969  * 
22970  */
22971 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22972
22973 /**
22974  * @class Roo.bootstrap.menu.Item
22975  * @extends Roo.bootstrap.Component
22976  * Bootstrap MenuItem class
22977  * @cfg {Boolean} submenu (true | false) default false
22978  * @cfg {String} html text of the item
22979  * @cfg {String} href the link
22980  * @cfg {Boolean} disable (true | false) default false
22981  * @cfg {Boolean} preventDefault (true | false) default true
22982  * @cfg {String} icon Font awesome icon
22983  * @cfg {String} pos Submenu align to (left | right) default right 
22984  * 
22985  * 
22986  * @constructor
22987  * Create a new Item
22988  * @param {Object} config The config object
22989  */
22990
22991
22992 Roo.bootstrap.menu.Item = function(config){
22993     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22994     this.addEvents({
22995         /**
22996          * @event mouseover
22997          * Fires when the mouse is hovering over this menu
22998          * @param {Roo.bootstrap.menu.Item} this
22999          * @param {Roo.EventObject} e
23000          */
23001         mouseover : true,
23002         /**
23003          * @event mouseout
23004          * Fires when the mouse exits this menu
23005          * @param {Roo.bootstrap.menu.Item} this
23006          * @param {Roo.EventObject} e
23007          */
23008         mouseout : true,
23009         // raw events
23010         /**
23011          * @event click
23012          * The raw click event for the entire grid.
23013          * @param {Roo.EventObject} e
23014          */
23015         click : true
23016     });
23017 };
23018
23019 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23020     
23021     submenu : false,
23022     href : '',
23023     html : '',
23024     preventDefault: true,
23025     disable : false,
23026     icon : false,
23027     pos : 'right',
23028     
23029     getAutoCreate : function()
23030     {
23031         var text = [
23032             {
23033                 tag : 'span',
23034                 cls : 'roo-menu-item-text',
23035                 html : this.html
23036             }
23037         ];
23038         
23039         if(this.icon){
23040             text.unshift({
23041                 tag : 'i',
23042                 cls : 'fa ' + this.icon
23043             })
23044         }
23045         
23046         var cfg = {
23047             tag : 'li',
23048             cn : [
23049                 {
23050                     tag : 'a',
23051                     href : this.href || '#',
23052                     cn : text
23053                 }
23054             ]
23055         };
23056         
23057         if(this.disable){
23058             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23059         }
23060         
23061         if(this.submenu){
23062             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23063             
23064             if(this.pos == 'left'){
23065                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23066             }
23067         }
23068         
23069         return cfg;
23070     },
23071     
23072     initEvents : function() 
23073     {
23074         this.el.on('mouseover', this.onMouseOver, this);
23075         this.el.on('mouseout', this.onMouseOut, this);
23076         
23077         this.el.select('a', true).first().on('click', this.onClick, this);
23078         
23079     },
23080     
23081     onClick : function(e)
23082     {
23083         if(this.preventDefault){
23084             e.preventDefault();
23085         }
23086         
23087         this.fireEvent("click", this, e);
23088     },
23089     
23090     onMouseOver : function(e)
23091     {
23092         if(this.submenu && this.pos == 'left'){
23093             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23094         }
23095         
23096         this.fireEvent("mouseover", this, e);
23097     },
23098     
23099     onMouseOut : function(e)
23100     {
23101         this.fireEvent("mouseout", this, e);
23102     }
23103 });
23104
23105  
23106
23107  /*
23108  * - LGPL
23109  *
23110  * menu separator
23111  * 
23112  */
23113 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23114
23115 /**
23116  * @class Roo.bootstrap.menu.Separator
23117  * @extends Roo.bootstrap.Component
23118  * Bootstrap Separator class
23119  * 
23120  * @constructor
23121  * Create a new Separator
23122  * @param {Object} config The config object
23123  */
23124
23125
23126 Roo.bootstrap.menu.Separator = function(config){
23127     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23128 };
23129
23130 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23131     
23132     getAutoCreate : function(){
23133         var cfg = {
23134             tag : 'li',
23135             cls: 'divider'
23136         };
23137         
23138         return cfg;
23139     }
23140    
23141 });
23142
23143  
23144
23145  /*
23146  * - LGPL
23147  *
23148  * Tooltip
23149  * 
23150  */
23151
23152 /**
23153  * @class Roo.bootstrap.Tooltip
23154  * Bootstrap Tooltip class
23155  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23156  * to determine which dom element triggers the tooltip.
23157  * 
23158  * It needs to add support for additional attributes like tooltip-position
23159  * 
23160  * @constructor
23161  * Create a new Toolti
23162  * @param {Object} config The config object
23163  */
23164
23165 Roo.bootstrap.Tooltip = function(config){
23166     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23167 };
23168
23169 Roo.apply(Roo.bootstrap.Tooltip, {
23170     /**
23171      * @function init initialize tooltip monitoring.
23172      * @static
23173      */
23174     currentEl : false,
23175     currentTip : false,
23176     currentRegion : false,
23177     
23178     //  init : delay?
23179     
23180     init : function()
23181     {
23182         Roo.get(document).on('mouseover', this.enter ,this);
23183         Roo.get(document).on('mouseout', this.leave, this);
23184          
23185         
23186         this.currentTip = new Roo.bootstrap.Tooltip();
23187     },
23188     
23189     enter : function(ev)
23190     {
23191         var dom = ev.getTarget();
23192         
23193         //Roo.log(['enter',dom]);
23194         var el = Roo.fly(dom);
23195         if (this.currentEl) {
23196             //Roo.log(dom);
23197             //Roo.log(this.currentEl);
23198             //Roo.log(this.currentEl.contains(dom));
23199             if (this.currentEl == el) {
23200                 return;
23201             }
23202             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23203                 return;
23204             }
23205
23206         }
23207         
23208         if (this.currentTip.el) {
23209             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23210         }    
23211         //Roo.log(ev);
23212         var bindEl = el;
23213         
23214         // you can not look for children, as if el is the body.. then everythign is the child..
23215         if (!el.attr('tooltip')) { //
23216             if (!el.select("[tooltip]").elements.length) {
23217                 return;
23218             }
23219             // is the mouse over this child...?
23220             bindEl = el.select("[tooltip]").first();
23221             var xy = ev.getXY();
23222             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23223                 //Roo.log("not in region.");
23224                 return;
23225             }
23226             //Roo.log("child element over..");
23227             
23228         }
23229         this.currentEl = bindEl;
23230         this.currentTip.bind(bindEl);
23231         this.currentRegion = Roo.lib.Region.getRegion(dom);
23232         this.currentTip.enter();
23233         
23234     },
23235     leave : function(ev)
23236     {
23237         var dom = ev.getTarget();
23238         //Roo.log(['leave',dom]);
23239         if (!this.currentEl) {
23240             return;
23241         }
23242         
23243         
23244         if (dom != this.currentEl.dom) {
23245             return;
23246         }
23247         var xy = ev.getXY();
23248         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23249             return;
23250         }
23251         // only activate leave if mouse cursor is outside... bounding box..
23252         
23253         
23254         
23255         
23256         if (this.currentTip) {
23257             this.currentTip.leave();
23258         }
23259         //Roo.log('clear currentEl');
23260         this.currentEl = false;
23261         
23262         
23263     },
23264     alignment : {
23265         'left' : ['r-l', [-2,0], 'right'],
23266         'right' : ['l-r', [2,0], 'left'],
23267         'bottom' : ['t-b', [0,2], 'top'],
23268         'top' : [ 'b-t', [0,-2], 'bottom']
23269     }
23270     
23271 });
23272
23273
23274 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23275     
23276     
23277     bindEl : false,
23278     
23279     delay : null, // can be { show : 300 , hide: 500}
23280     
23281     timeout : null,
23282     
23283     hoverState : null, //???
23284     
23285     placement : 'bottom', 
23286     
23287     getAutoCreate : function(){
23288     
23289         var cfg = {
23290            cls : 'tooltip',
23291            role : 'tooltip',
23292            cn : [
23293                 {
23294                     cls : 'tooltip-arrow'
23295                 },
23296                 {
23297                     cls : 'tooltip-inner'
23298                 }
23299            ]
23300         };
23301         
23302         return cfg;
23303     },
23304     bind : function(el)
23305     {
23306         this.bindEl = el;
23307     },
23308       
23309     
23310     enter : function () {
23311        
23312         if (this.timeout != null) {
23313             clearTimeout(this.timeout);
23314         }
23315         
23316         this.hoverState = 'in';
23317          //Roo.log("enter - show");
23318         if (!this.delay || !this.delay.show) {
23319             this.show();
23320             return;
23321         }
23322         var _t = this;
23323         this.timeout = setTimeout(function () {
23324             if (_t.hoverState == 'in') {
23325                 _t.show();
23326             }
23327         }, this.delay.show);
23328     },
23329     leave : function()
23330     {
23331         clearTimeout(this.timeout);
23332     
23333         this.hoverState = 'out';
23334          if (!this.delay || !this.delay.hide) {
23335             this.hide();
23336             return;
23337         }
23338        
23339         var _t = this;
23340         this.timeout = setTimeout(function () {
23341             //Roo.log("leave - timeout");
23342             
23343             if (_t.hoverState == 'out') {
23344                 _t.hide();
23345                 Roo.bootstrap.Tooltip.currentEl = false;
23346             }
23347         }, delay);
23348     },
23349     
23350     show : function ()
23351     {
23352         if (!this.el) {
23353             this.render(document.body);
23354         }
23355         // set content.
23356         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23357         
23358         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23359         
23360         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23361         
23362         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23363         
23364         var placement = typeof this.placement == 'function' ?
23365             this.placement.call(this, this.el, on_el) :
23366             this.placement;
23367             
23368         var autoToken = /\s?auto?\s?/i;
23369         var autoPlace = autoToken.test(placement);
23370         if (autoPlace) {
23371             placement = placement.replace(autoToken, '') || 'top';
23372         }
23373         
23374         //this.el.detach()
23375         //this.el.setXY([0,0]);
23376         this.el.show();
23377         //this.el.dom.style.display='block';
23378         
23379         //this.el.appendTo(on_el);
23380         
23381         var p = this.getPosition();
23382         var box = this.el.getBox();
23383         
23384         if (autoPlace) {
23385             // fixme..
23386         }
23387         
23388         var align = Roo.bootstrap.Tooltip.alignment[placement];
23389         
23390         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23391         
23392         if(placement == 'top' || placement == 'bottom'){
23393             if(xy[0] < 0){
23394                 placement = 'right';
23395             }
23396             
23397             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23398                 placement = 'left';
23399             }
23400         }
23401         
23402         align = Roo.bootstrap.Tooltip.alignment[placement];
23403         
23404         this.el.alignTo(this.bindEl, align[0],align[1]);
23405         //var arrow = this.el.select('.arrow',true).first();
23406         //arrow.set(align[2], 
23407         
23408         this.el.addClass(placement);
23409         
23410         this.el.addClass('in fade');
23411         
23412         this.hoverState = null;
23413         
23414         if (this.el.hasClass('fade')) {
23415             // fade it?
23416         }
23417         
23418     },
23419     hide : function()
23420     {
23421          
23422         if (!this.el) {
23423             return;
23424         }
23425         //this.el.setXY([0,0]);
23426         this.el.removeClass('in');
23427         //this.el.hide();
23428         
23429     }
23430     
23431 });
23432  
23433
23434  /*
23435  * - LGPL
23436  *
23437  * Location Picker
23438  * 
23439  */
23440
23441 /**
23442  * @class Roo.bootstrap.LocationPicker
23443  * @extends Roo.bootstrap.Component
23444  * Bootstrap LocationPicker class
23445  * @cfg {Number} latitude Position when init default 0
23446  * @cfg {Number} longitude Position when init default 0
23447  * @cfg {Number} zoom default 15
23448  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23449  * @cfg {Boolean} mapTypeControl default false
23450  * @cfg {Boolean} disableDoubleClickZoom default false
23451  * @cfg {Boolean} scrollwheel default true
23452  * @cfg {Boolean} streetViewControl default false
23453  * @cfg {Number} radius default 0
23454  * @cfg {String} locationName
23455  * @cfg {Boolean} draggable default true
23456  * @cfg {Boolean} enableAutocomplete default false
23457  * @cfg {Boolean} enableReverseGeocode default true
23458  * @cfg {String} markerTitle
23459  * 
23460  * @constructor
23461  * Create a new LocationPicker
23462  * @param {Object} config The config object
23463  */
23464
23465
23466 Roo.bootstrap.LocationPicker = function(config){
23467     
23468     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23469     
23470     this.addEvents({
23471         /**
23472          * @event initial
23473          * Fires when the picker initialized.
23474          * @param {Roo.bootstrap.LocationPicker} this
23475          * @param {Google Location} location
23476          */
23477         initial : true,
23478         /**
23479          * @event positionchanged
23480          * Fires when the picker position changed.
23481          * @param {Roo.bootstrap.LocationPicker} this
23482          * @param {Google Location} location
23483          */
23484         positionchanged : true,
23485         /**
23486          * @event resize
23487          * Fires when the map resize.
23488          * @param {Roo.bootstrap.LocationPicker} this
23489          */
23490         resize : true,
23491         /**
23492          * @event show
23493          * Fires when the map show.
23494          * @param {Roo.bootstrap.LocationPicker} this
23495          */
23496         show : true,
23497         /**
23498          * @event hide
23499          * Fires when the map hide.
23500          * @param {Roo.bootstrap.LocationPicker} this
23501          */
23502         hide : true,
23503         /**
23504          * @event mapClick
23505          * Fires when click the map.
23506          * @param {Roo.bootstrap.LocationPicker} this
23507          * @param {Map event} e
23508          */
23509         mapClick : true,
23510         /**
23511          * @event mapRightClick
23512          * Fires when right click the map.
23513          * @param {Roo.bootstrap.LocationPicker} this
23514          * @param {Map event} e
23515          */
23516         mapRightClick : true,
23517         /**
23518          * @event markerClick
23519          * Fires when click the marker.
23520          * @param {Roo.bootstrap.LocationPicker} this
23521          * @param {Map event} e
23522          */
23523         markerClick : true,
23524         /**
23525          * @event markerRightClick
23526          * Fires when right click the marker.
23527          * @param {Roo.bootstrap.LocationPicker} this
23528          * @param {Map event} e
23529          */
23530         markerRightClick : true,
23531         /**
23532          * @event OverlayViewDraw
23533          * Fires when OverlayView Draw
23534          * @param {Roo.bootstrap.LocationPicker} this
23535          */
23536         OverlayViewDraw : true,
23537         /**
23538          * @event OverlayViewOnAdd
23539          * Fires when OverlayView Draw
23540          * @param {Roo.bootstrap.LocationPicker} this
23541          */
23542         OverlayViewOnAdd : true,
23543         /**
23544          * @event OverlayViewOnRemove
23545          * Fires when OverlayView Draw
23546          * @param {Roo.bootstrap.LocationPicker} this
23547          */
23548         OverlayViewOnRemove : true,
23549         /**
23550          * @event OverlayViewShow
23551          * Fires when OverlayView Draw
23552          * @param {Roo.bootstrap.LocationPicker} this
23553          * @param {Pixel} cpx
23554          */
23555         OverlayViewShow : true,
23556         /**
23557          * @event OverlayViewHide
23558          * Fires when OverlayView Draw
23559          * @param {Roo.bootstrap.LocationPicker} this
23560          */
23561         OverlayViewHide : true
23562     });
23563         
23564 };
23565
23566 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23567     
23568     gMapContext: false,
23569     
23570     latitude: 0,
23571     longitude: 0,
23572     zoom: 15,
23573     mapTypeId: false,
23574     mapTypeControl: false,
23575     disableDoubleClickZoom: false,
23576     scrollwheel: true,
23577     streetViewControl: false,
23578     radius: 0,
23579     locationName: '',
23580     draggable: true,
23581     enableAutocomplete: false,
23582     enableReverseGeocode: true,
23583     markerTitle: '',
23584     
23585     getAutoCreate: function()
23586     {
23587
23588         var cfg = {
23589             tag: 'div',
23590             cls: 'roo-location-picker'
23591         };
23592         
23593         return cfg
23594     },
23595     
23596     initEvents: function(ct, position)
23597     {       
23598         if(!this.el.getWidth() || this.isApplied()){
23599             return;
23600         }
23601         
23602         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23603         
23604         this.initial();
23605     },
23606     
23607     initial: function()
23608     {
23609         if(!this.mapTypeId){
23610             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23611         }
23612         
23613         this.gMapContext = this.GMapContext();
23614         
23615         this.initOverlayView();
23616         
23617         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23618         
23619         var _this = this;
23620                 
23621         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23622             _this.setPosition(_this.gMapContext.marker.position);
23623         });
23624         
23625         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23626             _this.fireEvent('mapClick', this, event);
23627             
23628         });
23629
23630         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23631             _this.fireEvent('mapRightClick', this, event);
23632             
23633         });
23634         
23635         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23636             _this.fireEvent('markerClick', this, event);
23637             
23638         });
23639
23640         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23641             _this.fireEvent('markerRightClick', this, event);
23642             
23643         });
23644         
23645         this.setPosition(this.gMapContext.location);
23646         
23647         this.fireEvent('initial', this, this.gMapContext.location);
23648     },
23649     
23650     initOverlayView: function()
23651     {
23652         var _this = this;
23653         
23654         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23655             
23656             draw: function()
23657             {
23658                 _this.fireEvent('OverlayViewDraw', _this);
23659             },
23660             
23661             onAdd: function()
23662             {
23663                 _this.fireEvent('OverlayViewOnAdd', _this);
23664             },
23665             
23666             onRemove: function()
23667             {
23668                 _this.fireEvent('OverlayViewOnRemove', _this);
23669             },
23670             
23671             show: function(cpx)
23672             {
23673                 _this.fireEvent('OverlayViewShow', _this, cpx);
23674             },
23675             
23676             hide: function()
23677             {
23678                 _this.fireEvent('OverlayViewHide', _this);
23679             }
23680             
23681         });
23682     },
23683     
23684     fromLatLngToContainerPixel: function(event)
23685     {
23686         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23687     },
23688     
23689     isApplied: function() 
23690     {
23691         return this.getGmapContext() == false ? false : true;
23692     },
23693     
23694     getGmapContext: function() 
23695     {
23696         return this.gMapContext
23697     },
23698     
23699     GMapContext: function() 
23700     {
23701         var position = new google.maps.LatLng(this.latitude, this.longitude);
23702         
23703         var _map = new google.maps.Map(this.el.dom, {
23704             center: position,
23705             zoom: this.zoom,
23706             mapTypeId: this.mapTypeId,
23707             mapTypeControl: this.mapTypeControl,
23708             disableDoubleClickZoom: this.disableDoubleClickZoom,
23709             scrollwheel: this.scrollwheel,
23710             streetViewControl: this.streetViewControl,
23711             locationName: this.locationName,
23712             draggable: this.draggable,
23713             enableAutocomplete: this.enableAutocomplete,
23714             enableReverseGeocode: this.enableReverseGeocode
23715         });
23716         
23717         var _marker = new google.maps.Marker({
23718             position: position,
23719             map: _map,
23720             title: this.markerTitle,
23721             draggable: this.draggable
23722         });
23723         
23724         return {
23725             map: _map,
23726             marker: _marker,
23727             circle: null,
23728             location: position,
23729             radius: this.radius,
23730             locationName: this.locationName,
23731             addressComponents: {
23732                 formatted_address: null,
23733                 addressLine1: null,
23734                 addressLine2: null,
23735                 streetName: null,
23736                 streetNumber: null,
23737                 city: null,
23738                 district: null,
23739                 state: null,
23740                 stateOrProvince: null
23741             },
23742             settings: this,
23743             domContainer: this.el.dom,
23744             geodecoder: new google.maps.Geocoder()
23745         };
23746     },
23747     
23748     drawCircle: function(center, radius, options) 
23749     {
23750         if (this.gMapContext.circle != null) {
23751             this.gMapContext.circle.setMap(null);
23752         }
23753         if (radius > 0) {
23754             radius *= 1;
23755             options = Roo.apply({}, options, {
23756                 strokeColor: "#0000FF",
23757                 strokeOpacity: .35,
23758                 strokeWeight: 2,
23759                 fillColor: "#0000FF",
23760                 fillOpacity: .2
23761             });
23762             
23763             options.map = this.gMapContext.map;
23764             options.radius = radius;
23765             options.center = center;
23766             this.gMapContext.circle = new google.maps.Circle(options);
23767             return this.gMapContext.circle;
23768         }
23769         
23770         return null;
23771     },
23772     
23773     setPosition: function(location) 
23774     {
23775         this.gMapContext.location = location;
23776         this.gMapContext.marker.setPosition(location);
23777         this.gMapContext.map.panTo(location);
23778         this.drawCircle(location, this.gMapContext.radius, {});
23779         
23780         var _this = this;
23781         
23782         if (this.gMapContext.settings.enableReverseGeocode) {
23783             this.gMapContext.geodecoder.geocode({
23784                 latLng: this.gMapContext.location
23785             }, function(results, status) {
23786                 
23787                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23788                     _this.gMapContext.locationName = results[0].formatted_address;
23789                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23790                     
23791                     _this.fireEvent('positionchanged', this, location);
23792                 }
23793             });
23794             
23795             return;
23796         }
23797         
23798         this.fireEvent('positionchanged', this, location);
23799     },
23800     
23801     resize: function()
23802     {
23803         google.maps.event.trigger(this.gMapContext.map, "resize");
23804         
23805         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23806         
23807         this.fireEvent('resize', this);
23808     },
23809     
23810     setPositionByLatLng: function(latitude, longitude)
23811     {
23812         this.setPosition(new google.maps.LatLng(latitude, longitude));
23813     },
23814     
23815     getCurrentPosition: function() 
23816     {
23817         return {
23818             latitude: this.gMapContext.location.lat(),
23819             longitude: this.gMapContext.location.lng()
23820         };
23821     },
23822     
23823     getAddressName: function() 
23824     {
23825         return this.gMapContext.locationName;
23826     },
23827     
23828     getAddressComponents: function() 
23829     {
23830         return this.gMapContext.addressComponents;
23831     },
23832     
23833     address_component_from_google_geocode: function(address_components) 
23834     {
23835         var result = {};
23836         
23837         for (var i = 0; i < address_components.length; i++) {
23838             var component = address_components[i];
23839             if (component.types.indexOf("postal_code") >= 0) {
23840                 result.postalCode = component.short_name;
23841             } else if (component.types.indexOf("street_number") >= 0) {
23842                 result.streetNumber = component.short_name;
23843             } else if (component.types.indexOf("route") >= 0) {
23844                 result.streetName = component.short_name;
23845             } else if (component.types.indexOf("neighborhood") >= 0) {
23846                 result.city = component.short_name;
23847             } else if (component.types.indexOf("locality") >= 0) {
23848                 result.city = component.short_name;
23849             } else if (component.types.indexOf("sublocality") >= 0) {
23850                 result.district = component.short_name;
23851             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23852                 result.stateOrProvince = component.short_name;
23853             } else if (component.types.indexOf("country") >= 0) {
23854                 result.country = component.short_name;
23855             }
23856         }
23857         
23858         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23859         result.addressLine2 = "";
23860         return result;
23861     },
23862     
23863     setZoomLevel: function(zoom)
23864     {
23865         this.gMapContext.map.setZoom(zoom);
23866     },
23867     
23868     show: function()
23869     {
23870         if(!this.el){
23871             return;
23872         }
23873         
23874         this.el.show();
23875         
23876         this.resize();
23877         
23878         this.fireEvent('show', this);
23879     },
23880     
23881     hide: function()
23882     {
23883         if(!this.el){
23884             return;
23885         }
23886         
23887         this.el.hide();
23888         
23889         this.fireEvent('hide', this);
23890     }
23891     
23892 });
23893
23894 Roo.apply(Roo.bootstrap.LocationPicker, {
23895     
23896     OverlayView : function(map, options)
23897     {
23898         options = options || {};
23899         
23900         this.setMap(map);
23901     }
23902     
23903     
23904 });/*
23905  * - LGPL
23906  *
23907  * Alert
23908  * 
23909  */
23910
23911 /**
23912  * @class Roo.bootstrap.Alert
23913  * @extends Roo.bootstrap.Component
23914  * Bootstrap Alert class
23915  * @cfg {String} title The title of alert
23916  * @cfg {String} html The content of alert
23917  * @cfg {String} weight (  success | info | warning | danger )
23918  * @cfg {String} faicon font-awesomeicon
23919  * 
23920  * @constructor
23921  * Create a new alert
23922  * @param {Object} config The config object
23923  */
23924
23925
23926 Roo.bootstrap.Alert = function(config){
23927     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23928     
23929 };
23930
23931 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23932     
23933     title: '',
23934     html: '',
23935     weight: false,
23936     faicon: false,
23937     
23938     getAutoCreate : function()
23939     {
23940         
23941         var cfg = {
23942             tag : 'div',
23943             cls : 'alert',
23944             cn : [
23945                 {
23946                     tag : 'i',
23947                     cls : 'roo-alert-icon'
23948                     
23949                 },
23950                 {
23951                     tag : 'b',
23952                     cls : 'roo-alert-title',
23953                     html : this.title
23954                 },
23955                 {
23956                     tag : 'span',
23957                     cls : 'roo-alert-text',
23958                     html : this.html
23959                 }
23960             ]
23961         };
23962         
23963         if(this.faicon){
23964             cfg.cn[0].cls += ' fa ' + this.faicon;
23965         }
23966         
23967         if(this.weight){
23968             cfg.cls += ' alert-' + this.weight;
23969         }
23970         
23971         return cfg;
23972     },
23973     
23974     initEvents: function() 
23975     {
23976         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23977     },
23978     
23979     setTitle : function(str)
23980     {
23981         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23982     },
23983     
23984     setText : function(str)
23985     {
23986         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23987     },
23988     
23989     setWeight : function(weight)
23990     {
23991         if(this.weight){
23992             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23993         }
23994         
23995         this.weight = weight;
23996         
23997         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23998     },
23999     
24000     setIcon : function(icon)
24001     {
24002         if(this.faicon){
24003             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24004         }
24005         
24006         this.faicon = icon
24007         
24008         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24009     },
24010     
24011     hide: function() 
24012     {
24013         this.el.hide();   
24014     },
24015     
24016     show: function() 
24017     {  
24018         this.el.show();   
24019     }
24020     
24021 });
24022
24023  
24024 /*
24025 * Licence: LGPL
24026 */
24027
24028 /**
24029  * @class Roo.bootstrap.UploadCropbox
24030  * @extends Roo.bootstrap.Component
24031  * Bootstrap UploadCropbox class
24032  * @cfg {String} emptyText show when image has been loaded
24033  * @cfg {String} rotateNotify show when image too small to rotate
24034  * @cfg {Number} errorTimeout default 3000
24035  * @cfg {Number} minWidth default 300
24036  * @cfg {Number} minHeight default 300
24037  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24038  * @cfg {Boolean} isDocument (true|false) default false
24039  * 
24040  * @constructor
24041  * Create a new UploadCropbox
24042  * @param {Object} config The config object
24043  */
24044
24045 Roo.bootstrap.UploadCropbox = function(config){
24046     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24047     
24048     this.addEvents({
24049         /**
24050          * @event beforeselectfile
24051          * Fire before select file
24052          * @param {Roo.bootstrap.UploadCropbox} this
24053          */
24054         "beforeselectfile" : true,
24055         /**
24056          * @event initial
24057          * Fire after initEvent
24058          * @param {Roo.bootstrap.UploadCropbox} this
24059          */
24060         "initial" : true,
24061         /**
24062          * @event crop
24063          * Fire after initEvent
24064          * @param {Roo.bootstrap.UploadCropbox} this
24065          * @param {String} data
24066          */
24067         "crop" : true,
24068         /**
24069          * @event prepare
24070          * Fire when preparing the file data
24071          * @param {Roo.bootstrap.UploadCropbox} this
24072          * @param {Object} file
24073          */
24074         "prepare" : true,
24075         /**
24076          * @event exception
24077          * Fire when get exception
24078          * @param {Roo.bootstrap.UploadCropbox} this
24079          * @param {Object} options
24080          */
24081         "exception" : true,
24082         /**
24083          * @event beforeloadcanvas
24084          * Fire before load the canvas
24085          * @param {Roo.bootstrap.UploadCropbox} this
24086          * @param {String} src
24087          */
24088         "beforeloadcanvas" : true,
24089         /**
24090          * @event trash
24091          * Fire when trash image
24092          * @param {Roo.bootstrap.UploadCropbox} this
24093          */
24094         "trash" : true,
24095         /**
24096          * @event download
24097          * Fire when download the image
24098          * @param {Roo.bootstrap.UploadCropbox} this
24099          */
24100         "download" : true,
24101         /**
24102          * @event footerbuttonclick
24103          * Fire when footerbuttonclick
24104          * @param {Roo.bootstrap.UploadCropbox} this
24105          * @param {String} type
24106          */
24107         "footerbuttonclick" : true,
24108         /**
24109          * @event resize
24110          * Fire when resize
24111          * @param {Roo.bootstrap.UploadCropbox} this
24112          */
24113         "resize" : true,
24114         /**
24115          * @event rotate
24116          * Fire when rotate the image
24117          * @param {Roo.bootstrap.UploadCropbox} this
24118          * @param {String} pos
24119          */
24120         "rotate" : true
24121     });
24122     
24123     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24124 };
24125
24126 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24127     
24128     emptyText : 'Click to upload image',
24129     rotateNotify : 'Image is too small to rotate',
24130     errorTimeout : 3000,
24131     scale : 0,
24132     baseScale : 1,
24133     rotate : 0,
24134     dragable : false,
24135     pinching : false,
24136     mouseX : 0,
24137     mouseY : 0,
24138     cropData : false,
24139     minWidth : 300,
24140     minHeight : 300,
24141     file : false,
24142     exif : {},
24143     baseRotate : 1,
24144     cropType : 'image/jpeg',
24145     buttons : false,
24146     canvasLoaded : false,
24147     isDocument : false,
24148     
24149     getAutoCreate : function()
24150     {
24151         var cfg = {
24152             tag : 'div',
24153             cls : 'roo-upload-cropbox',
24154             cn : [
24155                 {
24156                     tag : 'div',
24157                     cls : 'roo-upload-cropbox-body',
24158                     style : 'cursor:pointer',
24159                     cn : [
24160                         {
24161                             tag : 'div',
24162                             cls : 'roo-upload-cropbox-preview'
24163                         },
24164                         {
24165                             tag : 'div',
24166                             cls : 'roo-upload-cropbox-thumb'
24167                         },
24168                         {
24169                             tag : 'div',
24170                             cls : 'roo-upload-cropbox-empty-notify',
24171                             html : this.emptyText
24172                         },
24173                         {
24174                             tag : 'div',
24175                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24176                             html : this.rotateNotify
24177                         }
24178                     ]
24179                 },
24180                 {
24181                     tag : 'div',
24182                     cls : 'roo-upload-cropbox-footer',
24183                     cn : {
24184                         tag : 'div',
24185                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24186                         cn : []
24187                     }
24188                 }
24189             ]
24190         };
24191         
24192         return cfg;
24193     },
24194     
24195     onRender : function(ct, position)
24196     {
24197         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24198         
24199         if (this.buttons.length) {
24200             
24201             Roo.each(this.buttons, function(bb) {
24202                 
24203                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24204                 
24205                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24206                 
24207             }, this);
24208         }
24209     },
24210     
24211     initEvents : function()
24212     {
24213         this.urlAPI = (window.createObjectURL && window) || 
24214                                 (window.URL && URL.revokeObjectURL && URL) || 
24215                                 (window.webkitURL && webkitURL);
24216                         
24217         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24218         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24219         
24220         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24221         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24222         
24223         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24224         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24225         this.thumbEl.hide();
24226         
24227         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24228         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24229         
24230         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24231         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24232         this.errorEl.hide();
24233         
24234         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24235         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24236         this.footerEl.hide();
24237         
24238         this.setThumbBoxSize();
24239         
24240         this.bind();
24241         
24242         this.resize();
24243         
24244         this.fireEvent('initial', this);
24245     },
24246
24247     bind : function()
24248     {
24249         var _this = this;
24250         
24251         window.addEventListener("resize", function() { _this.resize(); } );
24252         
24253         this.bodyEl.on('click', this.beforeSelectFile, this);
24254         
24255         if(Roo.isTouch){
24256             this.bodyEl.on('touchstart', this.onTouchStart, this);
24257             this.bodyEl.on('touchmove', this.onTouchMove, this);
24258             this.bodyEl.on('touchend', this.onTouchEnd, this);
24259         }
24260         
24261         if(!Roo.isTouch){
24262             this.bodyEl.on('mousedown', this.onMouseDown, this);
24263             this.bodyEl.on('mousemove', this.onMouseMove, this);
24264             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24265             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24266             Roo.get(document).on('mouseup', this.onMouseUp, this);
24267         }
24268     },
24269     
24270     reset : function()
24271     {    
24272         this.scale = 0;
24273         this.baseScale = 1;
24274         this.rotate = 0;
24275         this.baseRotate = 1;
24276         this.dragable = false;
24277         this.pinching = false;
24278         this.mouseX = 0;
24279         this.mouseY = 0;
24280         this.cropData = false;
24281         this.notifyEl.dom.innerHTML = this.emptyText;
24282         
24283     },
24284     
24285     resize : function()
24286     {
24287         if(this.fireEvent('resize', this) != false){
24288             this.setThumbBoxPosition();
24289             this.setCanvasPosition();
24290         }
24291     },
24292     
24293     onFooterButtonClick : function(e, el, o, type)
24294     {
24295         switch (type) {
24296             case 'rotate-left' :
24297                 this.onRotateLeft(e);
24298                 break;
24299             case 'rotate-right' :
24300                 this.onRotateRight(e);
24301                 break;
24302             case 'picture' :
24303                 this.beforeSelectFile(e);
24304                 break;
24305             case 'trash' :
24306                 this.trash(e);
24307                 break;
24308             case 'crop' :
24309                 this.crop(e);
24310                 break;
24311             case 'download' :
24312                 this.download(e);
24313                 break;
24314             default :
24315                 break;
24316         }
24317         
24318         this.fireEvent('footerbuttonclick', this, type);
24319     },
24320     
24321     beforeSelectFile : function(e)
24322     {
24323         this.fireEvent('beforeselectfile', this);
24324     },
24325     
24326     trash : function(e)
24327     {
24328         this.fireEvent('trash', this);
24329     },
24330     
24331     download : function(e)
24332     {
24333         this.fireEvent('download', this);
24334     },
24335     
24336     loadCanvas : function(src)
24337     {   
24338         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24339             
24340             this.reset();
24341             
24342             this.imageEl = document.createElement('img');
24343             
24344             var _this = this;
24345             
24346             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24347             
24348             this.imageEl.src = src;
24349         }
24350     },
24351     
24352     onLoadCanvas : function()
24353     {   
24354         this.bodyEl.un('click', this.beforeSelectFile, this);
24355         
24356         this.notifyEl.hide();
24357         this.thumbEl.show();
24358         this.footerEl.show();
24359         
24360         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24361         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24362         
24363         this.baseRotateLevel();
24364         
24365         if(this.isDocument){
24366             this.setThumbBoxSize();
24367         }
24368         
24369         this.setThumbBoxPosition();
24370         
24371         this.baseScaleLevel();
24372         
24373         this.draw();
24374         
24375         this.resize();
24376         
24377         this.canvasLoaded = true;
24378         
24379     },
24380     
24381     setCanvasPosition : function()
24382     {   
24383         if(!this.canvasEl){
24384             return;
24385         }
24386         
24387         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24388         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24389         
24390         this.previewEl.setLeft(pw);
24391         this.previewEl.setTop(ph);
24392         
24393     },
24394     
24395     onMouseDown : function(e)
24396     {   
24397         e.stopEvent();
24398         
24399         this.dragable = true;
24400         this.pinching = false;
24401         
24402         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24403             this.dragable = false;
24404             return;
24405         }
24406         
24407         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24408         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24409         
24410     },
24411     
24412     onMouseMove : function(e)
24413     {   
24414         e.stopEvent();
24415         
24416         if(!this.canvasLoaded){
24417             return;
24418         }
24419         
24420         if (!this.dragable){
24421             return;
24422         }
24423         
24424         var minX = Math.ceil(this.thumbEl.getLeft(true));
24425         var minY = Math.ceil(this.thumbEl.getTop(true));
24426         
24427         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24428         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24429         
24430         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24431         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24432         
24433         x = x - this.mouseX;
24434         y = y - this.mouseY;
24435         
24436         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24437         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24438         
24439         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24440         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24441         
24442         this.previewEl.setLeft(bgX);
24443         this.previewEl.setTop(bgY);
24444         
24445         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24446         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24447     },
24448     
24449     onMouseUp : function(e)
24450     {   
24451         e.stopEvent();
24452         
24453         this.dragable = false;
24454     },
24455     
24456     onMouseWheel : function(e)
24457     {   
24458         e.stopEvent();
24459         
24460         this.startScale = this.scale;
24461         
24462         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24463         
24464         if(!this.zoomable()){
24465             this.scale = this.startScale;
24466             return;
24467         }
24468         
24469         this.draw();
24470         
24471         return;
24472     },
24473     
24474     zoomable : function()
24475     {
24476         var minScale = this.thumbEl.getWidth() / this.minWidth;
24477         
24478         if(this.minWidth < this.minHeight){
24479             minScale = this.thumbEl.getHeight() / this.minHeight;
24480         }
24481         
24482         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24483         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24484         
24485         if(
24486                 this.isDocument &&
24487                 (this.rotate == 0 || this.rotate == 180) && 
24488                 (
24489                     width > this.imageEl.OriginWidth || 
24490                     height > this.imageEl.OriginHeight ||
24491                     (width < this.minWidth && height < this.minHeight)
24492                 )
24493         ){
24494             return false;
24495         }
24496         
24497         if(
24498                 this.isDocument &&
24499                 (this.rotate == 90 || this.rotate == 270) && 
24500                 (
24501                     width > this.imageEl.OriginWidth || 
24502                     height > this.imageEl.OriginHeight ||
24503                     (width < this.minHeight && height < this.minWidth)
24504                 )
24505         ){
24506             return false;
24507         }
24508         
24509         if(
24510                 !this.isDocument &&
24511                 (this.rotate == 0 || this.rotate == 180) && 
24512                 (
24513                     width < this.minWidth || 
24514                     width > this.imageEl.OriginWidth || 
24515                     height < this.minHeight || 
24516                     height > this.imageEl.OriginHeight
24517                 )
24518         ){
24519             return false;
24520         }
24521         
24522         if(
24523                 !this.isDocument &&
24524                 (this.rotate == 90 || this.rotate == 270) && 
24525                 (
24526                     width < this.minHeight || 
24527                     width > this.imageEl.OriginWidth || 
24528                     height < this.minWidth || 
24529                     height > this.imageEl.OriginHeight
24530                 )
24531         ){
24532             return false;
24533         }
24534         
24535         return true;
24536         
24537     },
24538     
24539     onRotateLeft : function(e)
24540     {   
24541         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24542             
24543             var minScale = this.thumbEl.getWidth() / this.minWidth;
24544             
24545             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24546             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24547             
24548             this.startScale = this.scale;
24549             
24550             while (this.getScaleLevel() < minScale){
24551             
24552                 this.scale = this.scale + 1;
24553                 
24554                 if(!this.zoomable()){
24555                     break;
24556                 }
24557                 
24558                 if(
24559                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24560                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24561                 ){
24562                     continue;
24563                 }
24564                 
24565                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24566
24567                 this.draw();
24568                 
24569                 return;
24570             }
24571             
24572             this.scale = this.startScale;
24573             
24574             this.onRotateFail();
24575             
24576             return false;
24577         }
24578         
24579         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24580
24581         if(this.isDocument){
24582             this.setThumbBoxSize();
24583             this.setThumbBoxPosition();
24584             this.setCanvasPosition();
24585         }
24586         
24587         this.draw();
24588         
24589         this.fireEvent('rotate', this, 'left');
24590         
24591     },
24592     
24593     onRotateRight : function(e)
24594     {
24595         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24596             
24597             var minScale = this.thumbEl.getWidth() / this.minWidth;
24598         
24599             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24600             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24601             
24602             this.startScale = this.scale;
24603             
24604             while (this.getScaleLevel() < minScale){
24605             
24606                 this.scale = this.scale + 1;
24607                 
24608                 if(!this.zoomable()){
24609                     break;
24610                 }
24611                 
24612                 if(
24613                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24614                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24615                 ){
24616                     continue;
24617                 }
24618                 
24619                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24620
24621                 this.draw();
24622                 
24623                 return;
24624             }
24625             
24626             this.scale = this.startScale;
24627             
24628             this.onRotateFail();
24629             
24630             return false;
24631         }
24632         
24633         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24634
24635         if(this.isDocument){
24636             this.setThumbBoxSize();
24637             this.setThumbBoxPosition();
24638             this.setCanvasPosition();
24639         }
24640         
24641         this.draw();
24642         
24643         this.fireEvent('rotate', this, 'right');
24644     },
24645     
24646     onRotateFail : function()
24647     {
24648         this.errorEl.show(true);
24649         
24650         var _this = this;
24651         
24652         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24653     },
24654     
24655     draw : function()
24656     {
24657         this.previewEl.dom.innerHTML = '';
24658         
24659         var canvasEl = document.createElement("canvas");
24660         
24661         var contextEl = canvasEl.getContext("2d");
24662         
24663         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24664         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24665         var center = this.imageEl.OriginWidth / 2;
24666         
24667         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24668             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24669             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24670             center = this.imageEl.OriginHeight / 2;
24671         }
24672         
24673         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24674         
24675         contextEl.translate(center, center);
24676         contextEl.rotate(this.rotate * Math.PI / 180);
24677
24678         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24679         
24680         this.canvasEl = document.createElement("canvas");
24681         
24682         this.contextEl = this.canvasEl.getContext("2d");
24683         
24684         switch (this.rotate) {
24685             case 0 :
24686                 
24687                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24688                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24689                 
24690                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24691                 
24692                 break;
24693             case 90 : 
24694                 
24695                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24696                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24697                 
24698                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24699                     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);
24700                     break;
24701                 }
24702                 
24703                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24704                 
24705                 break;
24706             case 180 :
24707                 
24708                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24709                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24710                 
24711                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24712                     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);
24713                     break;
24714                 }
24715                 
24716                 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);
24717                 
24718                 break;
24719             case 270 :
24720                 
24721                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24722                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24723         
24724                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24725                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24726                     break;
24727                 }
24728                 
24729                 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);
24730                 
24731                 break;
24732             default : 
24733                 break;
24734         }
24735         
24736         this.previewEl.appendChild(this.canvasEl);
24737         
24738         this.setCanvasPosition();
24739     },
24740     
24741     crop : function()
24742     {
24743         if(!this.canvasLoaded){
24744             return;
24745         }
24746         
24747         var imageCanvas = document.createElement("canvas");
24748         
24749         var imageContext = imageCanvas.getContext("2d");
24750         
24751         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24752         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24753         
24754         var center = imageCanvas.width / 2;
24755         
24756         imageContext.translate(center, center);
24757         
24758         imageContext.rotate(this.rotate * Math.PI / 180);
24759         
24760         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24761         
24762         var canvas = document.createElement("canvas");
24763         
24764         var context = canvas.getContext("2d");
24765                 
24766         canvas.width = this.minWidth;
24767         canvas.height = this.minHeight;
24768
24769         switch (this.rotate) {
24770             case 0 :
24771                 
24772                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24773                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24774                 
24775                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24776                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24777                 
24778                 var targetWidth = this.minWidth - 2 * x;
24779                 var targetHeight = this.minHeight - 2 * y;
24780                 
24781                 var scale = 1;
24782                 
24783                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24784                     scale = targetWidth / width;
24785                 }
24786                 
24787                 if(x > 0 && y == 0){
24788                     scale = targetHeight / height;
24789                 }
24790                 
24791                 if(x > 0 && y > 0){
24792                     scale = targetWidth / width;
24793                     
24794                     if(width < height){
24795                         scale = targetHeight / height;
24796                     }
24797                 }
24798                 
24799                 context.scale(scale, scale);
24800                 
24801                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24802                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24803
24804                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24805                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24806
24807                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24808                 
24809                 break;
24810             case 90 : 
24811                 
24812                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24813                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24814                 
24815                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24816                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24817                 
24818                 var targetWidth = this.minWidth - 2 * x;
24819                 var targetHeight = this.minHeight - 2 * y;
24820                 
24821                 var scale = 1;
24822                 
24823                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24824                     scale = targetWidth / width;
24825                 }
24826                 
24827                 if(x > 0 && y == 0){
24828                     scale = targetHeight / height;
24829                 }
24830                 
24831                 if(x > 0 && y > 0){
24832                     scale = targetWidth / width;
24833                     
24834                     if(width < height){
24835                         scale = targetHeight / height;
24836                     }
24837                 }
24838                 
24839                 context.scale(scale, scale);
24840                 
24841                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24842                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24843
24844                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24845                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24846                 
24847                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24848                 
24849                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24850                 
24851                 break;
24852             case 180 :
24853                 
24854                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24855                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24856                 
24857                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24858                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24859                 
24860                 var targetWidth = this.minWidth - 2 * x;
24861                 var targetHeight = this.minHeight - 2 * y;
24862                 
24863                 var scale = 1;
24864                 
24865                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24866                     scale = targetWidth / width;
24867                 }
24868                 
24869                 if(x > 0 && y == 0){
24870                     scale = targetHeight / height;
24871                 }
24872                 
24873                 if(x > 0 && y > 0){
24874                     scale = targetWidth / width;
24875                     
24876                     if(width < height){
24877                         scale = targetHeight / height;
24878                     }
24879                 }
24880                 
24881                 context.scale(scale, scale);
24882                 
24883                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24884                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24885
24886                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24887                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24888
24889                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
24890                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24891                 
24892                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24893                 
24894                 break;
24895             case 270 :
24896                 
24897                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24898                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24899                 
24900                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24901                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24902                 
24903                 var targetWidth = this.minWidth - 2 * x;
24904                 var targetHeight = this.minHeight - 2 * y;
24905                 
24906                 var scale = 1;
24907                 
24908                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24909                     scale = targetWidth / width;
24910                 }
24911                 
24912                 if(x > 0 && y == 0){
24913                     scale = targetHeight / height;
24914                 }
24915                 
24916                 if(x > 0 && y > 0){
24917                     scale = targetWidth / width;
24918                     
24919                     if(width < height){
24920                         scale = targetHeight / height;
24921                     }
24922                 }
24923                 
24924                 context.scale(scale, scale);
24925                 
24926                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24927                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24928
24929                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24930                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24931                 
24932                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
24933                 
24934                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24935                 
24936                 break;
24937             default : 
24938                 break;
24939         }
24940         
24941         this.cropData = canvas.toDataURL(this.cropType);
24942         
24943         this.fireEvent('crop', this, this.cropData);
24944         
24945     },
24946     
24947     setThumbBoxSize : function()
24948     {
24949         var width, height;
24950         
24951         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
24952             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
24953             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
24954             
24955             this.minWidth = width;
24956             this.minHeight = height;
24957             
24958             if(this.rotate == 90 || this.rotate == 270){
24959                 this.minWidth = height;
24960                 this.minHeight = width;
24961             }
24962         }
24963         
24964         height = 300;
24965         width = Math.ceil(this.minWidth * height / this.minHeight);
24966         
24967         if(this.minWidth > this.minHeight){
24968             width = 300;
24969             height = Math.ceil(this.minHeight * width / this.minWidth);
24970         }
24971         
24972         this.thumbEl.setStyle({
24973             width : width + 'px',
24974             height : height + 'px'
24975         });
24976
24977         return;
24978             
24979     },
24980     
24981     setThumbBoxPosition : function()
24982     {
24983         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24984         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24985         
24986         this.thumbEl.setLeft(x);
24987         this.thumbEl.setTop(y);
24988         
24989     },
24990     
24991     baseRotateLevel : function()
24992     {
24993         this.baseRotate = 1;
24994         
24995         if(
24996                 typeof(this.exif) != 'undefined' &&
24997                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24998                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24999         ){
25000             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25001         }
25002         
25003         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25004         
25005     },
25006     
25007     baseScaleLevel : function()
25008     {
25009         var width, height;
25010         
25011         if(this.isDocument){
25012             
25013             if(this.baseRotate == 6 || this.baseRotate == 8){
25014             
25015                 height = this.thumbEl.getHeight();
25016                 this.baseScale = height / this.imageEl.OriginWidth;
25017
25018                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25019                     width = this.thumbEl.getWidth();
25020                     this.baseScale = width / this.imageEl.OriginHeight;
25021                 }
25022
25023                 return;
25024             }
25025
25026             height = this.thumbEl.getHeight();
25027             this.baseScale = height / this.imageEl.OriginHeight;
25028
25029             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25030                 width = this.thumbEl.getWidth();
25031                 this.baseScale = width / this.imageEl.OriginWidth;
25032             }
25033
25034             return;
25035         }
25036         
25037         if(this.baseRotate == 6 || this.baseRotate == 8){
25038             
25039             width = this.thumbEl.getHeight();
25040             this.baseScale = width / this.imageEl.OriginHeight;
25041             
25042             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25043                 height = this.thumbEl.getWidth();
25044                 this.baseScale = height / this.imageEl.OriginHeight;
25045             }
25046             
25047             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25048                 height = this.thumbEl.getWidth();
25049                 this.baseScale = height / this.imageEl.OriginHeight;
25050                 
25051                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25052                     width = this.thumbEl.getHeight();
25053                     this.baseScale = width / this.imageEl.OriginWidth;
25054                 }
25055             }
25056             
25057             return;
25058         }
25059         
25060         width = this.thumbEl.getWidth();
25061         this.baseScale = width / this.imageEl.OriginWidth;
25062         
25063         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25064             height = this.thumbEl.getHeight();
25065             this.baseScale = height / this.imageEl.OriginHeight;
25066         }
25067         
25068         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25069             
25070             height = this.thumbEl.getHeight();
25071             this.baseScale = height / this.imageEl.OriginHeight;
25072             
25073             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25074                 width = this.thumbEl.getWidth();
25075                 this.baseScale = width / this.imageEl.OriginWidth;
25076             }
25077             
25078         }
25079         
25080         return;
25081     },
25082     
25083     getScaleLevel : function()
25084     {
25085         return this.baseScale * Math.pow(1.1, this.scale);
25086     },
25087     
25088     onTouchStart : function(e)
25089     {
25090         if(!this.canvasLoaded){
25091             this.beforeSelectFile(e);
25092             return;
25093         }
25094         
25095         var touches = e.browserEvent.touches;
25096         
25097         if(!touches){
25098             return;
25099         }
25100         
25101         if(touches.length == 1){
25102             this.onMouseDown(e);
25103             return;
25104         }
25105         
25106         if(touches.length != 2){
25107             return;
25108         }
25109         
25110         var coords = [];
25111         
25112         for(var i = 0, finger; finger = touches[i]; i++){
25113             coords.push(finger.pageX, finger.pageY);
25114         }
25115         
25116         var x = Math.pow(coords[0] - coords[2], 2);
25117         var y = Math.pow(coords[1] - coords[3], 2);
25118         
25119         this.startDistance = Math.sqrt(x + y);
25120         
25121         this.startScale = this.scale;
25122         
25123         this.pinching = true;
25124         this.dragable = false;
25125         
25126     },
25127     
25128     onTouchMove : function(e)
25129     {
25130         if(!this.pinching && !this.dragable){
25131             return;
25132         }
25133         
25134         var touches = e.browserEvent.touches;
25135         
25136         if(!touches){
25137             return;
25138         }
25139         
25140         if(this.dragable){
25141             this.onMouseMove(e);
25142             return;
25143         }
25144         
25145         var coords = [];
25146         
25147         for(var i = 0, finger; finger = touches[i]; i++){
25148             coords.push(finger.pageX, finger.pageY);
25149         }
25150         
25151         var x = Math.pow(coords[0] - coords[2], 2);
25152         var y = Math.pow(coords[1] - coords[3], 2);
25153         
25154         this.endDistance = Math.sqrt(x + y);
25155         
25156         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25157         
25158         if(!this.zoomable()){
25159             this.scale = this.startScale;
25160             return;
25161         }
25162         
25163         this.draw();
25164         
25165     },
25166     
25167     onTouchEnd : function(e)
25168     {
25169         this.pinching = false;
25170         this.dragable = false;
25171         
25172     },
25173     
25174     prepare : function(file)
25175     {   
25176         this.file = false;
25177         this.exif = {};
25178         
25179         if(typeof(file) === 'string'){
25180             this.loadCanvas(file);
25181             return;
25182         }
25183         
25184         if(!file || !this.urlAPI){
25185             return;
25186         }
25187         
25188         this.file = file;
25189         this.cropType = file.type;
25190         
25191         var _this = this;
25192         
25193         if(this.fireEvent('prepare', this, this.file) != false){
25194             
25195             var reader = new FileReader();
25196             
25197             reader.onload = function (e) {
25198                 if (e.target.error) {
25199                     Roo.log(e.target.error);
25200                     return;
25201                 }
25202                 
25203                 var buffer = e.target.result,
25204                     dataView = new DataView(buffer),
25205                     offset = 2,
25206                     maxOffset = dataView.byteLength - 4,
25207                     markerBytes,
25208                     markerLength;
25209                 
25210                 if (dataView.getUint16(0) === 0xffd8) {
25211                     while (offset < maxOffset) {
25212                         markerBytes = dataView.getUint16(offset);
25213                         
25214                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25215                             markerLength = dataView.getUint16(offset + 2) + 2;
25216                             if (offset + markerLength > dataView.byteLength) {
25217                                 Roo.log('Invalid meta data: Invalid segment size.');
25218                                 break;
25219                             }
25220                             
25221                             if(markerBytes == 0xffe1){
25222                                 _this.parseExifData(
25223                                     dataView,
25224                                     offset,
25225                                     markerLength
25226                                 );
25227                             }
25228                             
25229                             offset += markerLength;
25230                             
25231                             continue;
25232                         }
25233                         
25234                         break;
25235                     }
25236                     
25237                 }
25238                 
25239                 var url = _this.urlAPI.createObjectURL(_this.file);
25240                 
25241                 _this.loadCanvas(url);
25242                 
25243                 return;
25244             }
25245             
25246             reader.readAsArrayBuffer(this.file);
25247             
25248         }
25249         
25250     },
25251     
25252     parseExifData : function(dataView, offset, length)
25253     {
25254         var tiffOffset = offset + 10,
25255             littleEndian,
25256             dirOffset;
25257     
25258         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25259             // No Exif data, might be XMP data instead
25260             return;
25261         }
25262         
25263         // Check for the ASCII code for "Exif" (0x45786966):
25264         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25265             // No Exif data, might be XMP data instead
25266             return;
25267         }
25268         if (tiffOffset + 8 > dataView.byteLength) {
25269             Roo.log('Invalid Exif data: Invalid segment size.');
25270             return;
25271         }
25272         // Check for the two null bytes:
25273         if (dataView.getUint16(offset + 8) !== 0x0000) {
25274             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25275             return;
25276         }
25277         // Check the byte alignment:
25278         switch (dataView.getUint16(tiffOffset)) {
25279         case 0x4949:
25280             littleEndian = true;
25281             break;
25282         case 0x4D4D:
25283             littleEndian = false;
25284             break;
25285         default:
25286             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25287             return;
25288         }
25289         // Check for the TIFF tag marker (0x002A):
25290         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25291             Roo.log('Invalid Exif data: Missing TIFF marker.');
25292             return;
25293         }
25294         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25295         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25296         
25297         this.parseExifTags(
25298             dataView,
25299             tiffOffset,
25300             tiffOffset + dirOffset,
25301             littleEndian
25302         );
25303     },
25304     
25305     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25306     {
25307         var tagsNumber,
25308             dirEndOffset,
25309             i;
25310         if (dirOffset + 6 > dataView.byteLength) {
25311             Roo.log('Invalid Exif data: Invalid directory offset.');
25312             return;
25313         }
25314         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25315         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25316         if (dirEndOffset + 4 > dataView.byteLength) {
25317             Roo.log('Invalid Exif data: Invalid directory size.');
25318             return;
25319         }
25320         for (i = 0; i < tagsNumber; i += 1) {
25321             this.parseExifTag(
25322                 dataView,
25323                 tiffOffset,
25324                 dirOffset + 2 + 12 * i, // tag offset
25325                 littleEndian
25326             );
25327         }
25328         // Return the offset to the next directory:
25329         return dataView.getUint32(dirEndOffset, littleEndian);
25330     },
25331     
25332     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25333     {
25334         var tag = dataView.getUint16(offset, littleEndian);
25335         
25336         this.exif[tag] = this.getExifValue(
25337             dataView,
25338             tiffOffset,
25339             offset,
25340             dataView.getUint16(offset + 2, littleEndian), // tag type
25341             dataView.getUint32(offset + 4, littleEndian), // tag length
25342             littleEndian
25343         );
25344     },
25345     
25346     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25347     {
25348         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25349             tagSize,
25350             dataOffset,
25351             values,
25352             i,
25353             str,
25354             c;
25355     
25356         if (!tagType) {
25357             Roo.log('Invalid Exif data: Invalid tag type.');
25358             return;
25359         }
25360         
25361         tagSize = tagType.size * length;
25362         // Determine if the value is contained in the dataOffset bytes,
25363         // or if the value at the dataOffset is a pointer to the actual data:
25364         dataOffset = tagSize > 4 ?
25365                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25366         if (dataOffset + tagSize > dataView.byteLength) {
25367             Roo.log('Invalid Exif data: Invalid data offset.');
25368             return;
25369         }
25370         if (length === 1) {
25371             return tagType.getValue(dataView, dataOffset, littleEndian);
25372         }
25373         values = [];
25374         for (i = 0; i < length; i += 1) {
25375             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25376         }
25377         
25378         if (tagType.ascii) {
25379             str = '';
25380             // Concatenate the chars:
25381             for (i = 0; i < values.length; i += 1) {
25382                 c = values[i];
25383                 // Ignore the terminating NULL byte(s):
25384                 if (c === '\u0000') {
25385                     break;
25386                 }
25387                 str += c;
25388             }
25389             return str;
25390         }
25391         return values;
25392     }
25393     
25394 });
25395
25396 Roo.apply(Roo.bootstrap.UploadCropbox, {
25397     tags : {
25398         'Orientation': 0x0112
25399     },
25400     
25401     Orientation: {
25402             1: 0, //'top-left',
25403 //            2: 'top-right',
25404             3: 180, //'bottom-right',
25405 //            4: 'bottom-left',
25406 //            5: 'left-top',
25407             6: 90, //'right-top',
25408 //            7: 'right-bottom',
25409             8: 270 //'left-bottom'
25410     },
25411     
25412     exifTagTypes : {
25413         // byte, 8-bit unsigned int:
25414         1: {
25415             getValue: function (dataView, dataOffset) {
25416                 return dataView.getUint8(dataOffset);
25417             },
25418             size: 1
25419         },
25420         // ascii, 8-bit byte:
25421         2: {
25422             getValue: function (dataView, dataOffset) {
25423                 return String.fromCharCode(dataView.getUint8(dataOffset));
25424             },
25425             size: 1,
25426             ascii: true
25427         },
25428         // short, 16 bit int:
25429         3: {
25430             getValue: function (dataView, dataOffset, littleEndian) {
25431                 return dataView.getUint16(dataOffset, littleEndian);
25432             },
25433             size: 2
25434         },
25435         // long, 32 bit int:
25436         4: {
25437             getValue: function (dataView, dataOffset, littleEndian) {
25438                 return dataView.getUint32(dataOffset, littleEndian);
25439             },
25440             size: 4
25441         },
25442         // rational = two long values, first is numerator, second is denominator:
25443         5: {
25444             getValue: function (dataView, dataOffset, littleEndian) {
25445                 return dataView.getUint32(dataOffset, littleEndian) /
25446                     dataView.getUint32(dataOffset + 4, littleEndian);
25447             },
25448             size: 8
25449         },
25450         // slong, 32 bit signed int:
25451         9: {
25452             getValue: function (dataView, dataOffset, littleEndian) {
25453                 return dataView.getInt32(dataOffset, littleEndian);
25454             },
25455             size: 4
25456         },
25457         // srational, two slongs, first is numerator, second is denominator:
25458         10: {
25459             getValue: function (dataView, dataOffset, littleEndian) {
25460                 return dataView.getInt32(dataOffset, littleEndian) /
25461                     dataView.getInt32(dataOffset + 4, littleEndian);
25462             },
25463             size: 8
25464         }
25465     },
25466     
25467     footer : {
25468         STANDARD : [
25469             {
25470                 tag : 'div',
25471                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25472                 action : 'rotate-left',
25473                 cn : [
25474                     {
25475                         tag : 'button',
25476                         cls : 'btn btn-default',
25477                         html : '<i class="fa fa-undo"></i>'
25478                     }
25479                 ]
25480             },
25481             {
25482                 tag : 'div',
25483                 cls : 'btn-group roo-upload-cropbox-picture',
25484                 action : 'picture',
25485                 cn : [
25486                     {
25487                         tag : 'button',
25488                         cls : 'btn btn-default',
25489                         html : '<i class="fa fa-picture-o"></i>'
25490                     }
25491                 ]
25492             },
25493             {
25494                 tag : 'div',
25495                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25496                 action : 'rotate-right',
25497                 cn : [
25498                     {
25499                         tag : 'button',
25500                         cls : 'btn btn-default',
25501                         html : '<i class="fa fa-repeat"></i>'
25502                     }
25503                 ]
25504             }
25505         ],
25506         DOCUMENT : [
25507             {
25508                 tag : 'div',
25509                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25510                 action : 'rotate-left',
25511                 cn : [
25512                     {
25513                         tag : 'button',
25514                         cls : 'btn btn-default',
25515                         html : '<i class="fa fa-undo"></i>'
25516                     }
25517                 ]
25518             },
25519             {
25520                 tag : 'div',
25521                 cls : 'btn-group roo-upload-cropbox-download',
25522                 action : 'download',
25523                 cn : [
25524                     {
25525                         tag : 'button',
25526                         cls : 'btn btn-default',
25527                         html : '<i class="fa fa-download"></i>'
25528                     }
25529                 ]
25530             },
25531             {
25532                 tag : 'div',
25533                 cls : 'btn-group roo-upload-cropbox-crop',
25534                 action : 'crop',
25535                 cn : [
25536                     {
25537                         tag : 'button',
25538                         cls : 'btn btn-default',
25539                         html : '<i class="fa fa-crop"></i>'
25540                     }
25541                 ]
25542             },
25543             {
25544                 tag : 'div',
25545                 cls : 'btn-group roo-upload-cropbox-trash',
25546                 action : 'trash',
25547                 cn : [
25548                     {
25549                         tag : 'button',
25550                         cls : 'btn btn-default',
25551                         html : '<i class="fa fa-trash"></i>'
25552                     }
25553                 ]
25554             },
25555             {
25556                 tag : 'div',
25557                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25558                 action : 'rotate-right',
25559                 cn : [
25560                     {
25561                         tag : 'button',
25562                         cls : 'btn btn-default',
25563                         html : '<i class="fa fa-repeat"></i>'
25564                     }
25565                 ]
25566             }
25567         ],
25568         ROTATOR : [
25569             {
25570                 tag : 'div',
25571                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25572                 action : 'rotate-left',
25573                 cn : [
25574                     {
25575                         tag : 'button',
25576                         cls : 'btn btn-default',
25577                         html : '<i class="fa fa-undo"></i>'
25578                     }
25579                 ]
25580             },
25581             {
25582                 tag : 'div',
25583                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25584                 action : 'rotate-right',
25585                 cn : [
25586                     {
25587                         tag : 'button',
25588                         cls : 'btn btn-default',
25589                         html : '<i class="fa fa-repeat"></i>'
25590                     }
25591                 ]
25592             }
25593         ]
25594     }
25595 });
25596
25597 /*
25598 * Licence: LGPL
25599 */
25600
25601 /**
25602  * @class Roo.bootstrap.DocumentManager
25603  * @extends Roo.bootstrap.Component
25604  * Bootstrap DocumentManager class
25605  * @cfg {String} paramName default 'imageUpload'
25606  * @cfg {String} method default POST
25607  * @cfg {String} url action url
25608  * @cfg {Number} boxes number of boxes default 12
25609  * @cfg {Boolean} multiple multiple upload default true
25610  * @cfg {Number} minWidth default 300
25611  * @cfg {Number} minHeight default 300
25612  * @cfg {Number} thumbSize default 300
25613  * @cfg {String} fieldLabel
25614  * @cfg {Number} labelWidth default 4
25615  * @cfg {String} labelAlign (left|top) default left
25616  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25617  * 
25618  * @constructor
25619  * Create a new DocumentManager
25620  * @param {Object} config The config object
25621  */
25622
25623 Roo.bootstrap.DocumentManager = function(config){
25624     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25625     
25626     this.addEvents({
25627         /**
25628          * @event initial
25629          * Fire when initial the DocumentManager
25630          * @param {Roo.bootstrap.DocumentManager} this
25631          */
25632         "initial" : true,
25633         /**
25634          * @event inspect
25635          * inspect selected file
25636          * @param {Roo.bootstrap.DocumentManager} this
25637          * @param {File} file
25638          */
25639         "inspect" : true,
25640         /**
25641          * @event exception
25642          * Fire when xhr load exception
25643          * @param {Roo.bootstrap.DocumentManager} this
25644          * @param {XMLHttpRequest} xhr
25645          */
25646         "exception" : true,
25647         /**
25648          * @event prepare
25649          * prepare the form data
25650          * @param {Roo.bootstrap.DocumentManager} this
25651          * @param {Object} formData
25652          */
25653         "prepare" : true,
25654         /**
25655          * @event remove
25656          * Fire when remove the file
25657          * @param {Roo.bootstrap.DocumentManager} this
25658          * @param {Object} file
25659          */
25660         "remove" : true,
25661         /**
25662          * @event refresh
25663          * Fire after refresh the file
25664          * @param {Roo.bootstrap.DocumentManager} this
25665          */
25666         "refresh" : true,
25667         /**
25668          * @event click
25669          * Fire after click the image
25670          * @param {Roo.bootstrap.DocumentManager} this
25671          * @param {Object} file
25672          */
25673         "click" : true,
25674         /**
25675          * @event edit
25676          * Fire when upload a image and editable set to true
25677          * @param {Roo.bootstrap.DocumentManager} this
25678          * @param {Object} file
25679          */
25680         "edit" : true,
25681         /**
25682          * @event beforeselectfile
25683          * Fire before select file
25684          * @param {Roo.bootstrap.DocumentManager} this
25685          */
25686         "beforeselectfile" : true
25687         
25688     });
25689 };
25690
25691 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25692     
25693     boxes : 12,
25694     inputName : '',
25695     minWidth : 300,
25696     minHeight : 300,
25697     thumbSize : 300,
25698     multiple : true,
25699     files : [],
25700     method : 'POST',
25701     url : '',
25702     paramName : 'imageUpload',
25703     fieldLabel : '',
25704     labelWidth : 4,
25705     labelAlign : 'left',
25706     editable : true,
25707     delegates : [],
25708     
25709     getAutoCreate : function()
25710     {   
25711         var managerWidget = {
25712             tag : 'div',
25713             cls : 'roo-document-manager',
25714             cn : [
25715                 {
25716                     tag : 'input',
25717                     cls : 'roo-document-manager-selector',
25718                     type : 'file'
25719                 },
25720                 {
25721                     tag : 'div',
25722                     cls : 'roo-document-manager-uploader',
25723                     cn : [
25724                         {
25725                             tag : 'div',
25726                             cls : 'roo-document-manager-upload-btn',
25727                             html : '<i class="fa fa-plus"></i>'
25728                         }
25729                     ]
25730                     
25731                 }
25732             ]
25733         };
25734         
25735         var content = [
25736             {
25737                 tag : 'div',
25738                 cls : 'column col-md-12',
25739                 cn : managerWidget
25740             }
25741         ];
25742         
25743         if(this.fieldLabel.length){
25744             
25745             content = [
25746                 {
25747                     tag : 'div',
25748                     cls : 'column col-md-12',
25749                     html : this.fieldLabel
25750                 },
25751                 {
25752                     tag : 'div',
25753                     cls : 'column col-md-12',
25754                     cn : managerWidget
25755                 }
25756             ];
25757
25758             if(this.labelAlign == 'left'){
25759                 content = [
25760                     {
25761                         tag : 'div',
25762                         cls : 'column col-md-' + this.labelWidth,
25763                         html : this.fieldLabel
25764                     },
25765                     {
25766                         tag : 'div',
25767                         cls : 'column col-md-' + (12 - this.labelWidth),
25768                         cn : managerWidget
25769                     }
25770                 ];
25771                 
25772             }
25773         }
25774         
25775         var cfg = {
25776             tag : 'div',
25777             cls : 'row clearfix',
25778             cn : content
25779         };
25780         
25781         return cfg;
25782         
25783     },
25784     
25785     initEvents : function()
25786     {
25787         this.managerEl = this.el.select('.roo-document-manager', true).first();
25788         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25789         
25790         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25791         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25792         this.selectorEl.hide();
25793         
25794         if(this.multiple){
25795             this.selectorEl.attr('multiple', 'multiple');
25796         }
25797         
25798         this.selectorEl.on('change', this.onFileSelected, this);
25799         
25800         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25801         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25802         
25803         this.uploader.on('click', this.onUploaderClick, this);
25804         
25805         this.renderProgressDialog();
25806         
25807         var _this = this;
25808         
25809         window.addEventListener("resize", function() { _this.refresh(); } );
25810         
25811         this.fireEvent('initial', this);
25812     },
25813     
25814     renderProgressDialog : function()
25815     {
25816         var _this = this;
25817         
25818         this.progressDialog = new Roo.bootstrap.Modal({
25819             cls : 'roo-document-manager-progress-dialog',
25820             allow_close : false,
25821             title : '',
25822             buttons : [
25823                 {
25824                     name  :'cancel',
25825                     weight : 'danger',
25826                     html : 'Cancel'
25827                 }
25828             ], 
25829             listeners : { 
25830                 btnclick : function() {
25831                     _this.uploadCancel();
25832                     this.hide();
25833                 }
25834             }
25835         });
25836          
25837         this.progressDialog.render(Roo.get(document.body));
25838          
25839         this.progress = new Roo.bootstrap.Progress({
25840             cls : 'roo-document-manager-progress',
25841             active : true,
25842             striped : true
25843         });
25844         
25845         this.progress.render(this.progressDialog.getChildContainer());
25846         
25847         this.progressBar = new Roo.bootstrap.ProgressBar({
25848             cls : 'roo-document-manager-progress-bar',
25849             aria_valuenow : 0,
25850             aria_valuemin : 0,
25851             aria_valuemax : 12,
25852             panel : 'success'
25853         });
25854         
25855         this.progressBar.render(this.progress.getChildContainer());
25856     },
25857     
25858     onUploaderClick : function(e)
25859     {
25860         e.preventDefault();
25861         
25862         if(this.fireEvent('beforeselectfile', this) != false){
25863             this.selectorEl.dom.click();
25864         }
25865         
25866     },
25867     
25868     onFileSelected : function(e)
25869     {
25870         e.preventDefault();
25871         
25872         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25873             return;
25874         }
25875         
25876         Roo.each(this.selectorEl.dom.files, function(file){
25877             if(this.fireEvent('inspect', this, file) != false){
25878                 this.files.push(file);
25879             }
25880         }, this);
25881         
25882         this.queue();
25883         
25884     },
25885     
25886     queue : function()
25887     {
25888         this.selectorEl.dom.value = '';
25889         
25890         if(!this.files.length){
25891             return;
25892         }
25893         
25894         if(this.files.length > this.boxes){
25895             this.files = this.files.slice(0, this.boxes);
25896         }
25897         
25898         this.uploader.show();
25899         
25900         if(this.files.length > this.boxes - 1){
25901             this.uploader.hide();
25902         }
25903         
25904         var _this = this;
25905         
25906         var files = [];
25907         
25908         Roo.each(this.files, function(file){
25909             
25910             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25911                 var f = this.renderPreview(file);
25912                 files.push(f);
25913                 return;
25914             }
25915             
25916             this.delegates.push(
25917                 (function(){
25918                     _this.process(file);
25919                 }).createDelegate(this)
25920             );
25921             
25922         }, this);
25923         
25924         this.files = files;
25925         
25926         if(!this.delegates.length){
25927             this.refresh();
25928             return;
25929         }
25930         
25931         this.progressBar.aria_valuemax = this.delegates.length;
25932         
25933         this.arrange();
25934         
25935         return;
25936     },
25937     
25938     arrange : function()
25939     {
25940         if(!this.delegates.length){
25941             this.progressDialog.hide();
25942             this.refresh();
25943             return;
25944         }
25945         
25946         var delegate = this.delegates.shift();
25947         
25948         this.progressDialog.show();
25949         
25950         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
25951         
25952         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
25953         
25954         delegate();
25955     },
25956     
25957     refresh : function()
25958     {
25959         this.uploader.show();
25960         
25961         if(this.files.length > this.boxes - 1){
25962             this.uploader.hide();
25963         }
25964         
25965         Roo.isTouch ? this.closable(false) : this.closable(true);
25966         
25967         this.fireEvent('refresh', this);
25968     },
25969     
25970     onRemove : function(e, el, o)
25971     {
25972         e.preventDefault();
25973         
25974         this.fireEvent('remove', this, o);
25975         
25976     },
25977     
25978     remove : function(o)
25979     {
25980         var files = [];
25981         
25982         Roo.each(this.files, function(file){
25983             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25984                 files.push(file);
25985                 return;
25986             }
25987
25988             o.target.remove();
25989
25990         }, this);
25991         
25992         this.files = files;
25993         
25994         this.refresh();
25995     },
25996     
25997     onClick : function(e, el, o)
25998     {
25999         e.preventDefault();
26000         
26001         this.fireEvent('click', this, o);
26002         
26003     },
26004     
26005     closable : function(closable)
26006     {
26007         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26008             
26009             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26010             
26011             if(closable){
26012                 el.show();
26013                 return;
26014             }
26015             
26016             el.hide();
26017             
26018         }, this);
26019     },
26020     
26021     xhrOnLoad : function(xhr)
26022     {
26023         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26024             el.remove();
26025         }, this);
26026         
26027         if (xhr.readyState !== 4) {
26028             this.arrange();
26029             this.fireEvent('exception', this, xhr);
26030             return;
26031         }
26032
26033         var response = Roo.decode(xhr.responseText);
26034         
26035         if(!response.success){
26036             this.arrange();
26037             this.fireEvent('exception', this, xhr);
26038             return;
26039         }
26040         
26041         var file = this.renderPreview(response.data);
26042         
26043         this.files.push(file);
26044         
26045         this.arrange();
26046         
26047     },
26048     
26049     xhrOnError : function()
26050     {
26051         Roo.log('xhr on error');
26052         
26053         var response = Roo.decode(xhr.responseText);
26054           
26055         Roo.log(response);
26056         
26057         this.arrange();
26058     },
26059     
26060     process : function(file)
26061     {
26062         if(this.editable && file.type.indexOf('image') != -1){
26063             this.fireEvent('edit', this, file);
26064             return;
26065         }
26066         
26067         this.uploadStart(file, false);
26068         
26069         return;
26070     },
26071     
26072     uploadStart : function(file, crop)
26073     {
26074         this.xhr = new XMLHttpRequest();
26075         
26076         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26077             this.arrange();
26078             return;
26079         }
26080         
26081         file.xhr = this.xhr;
26082             
26083         this.managerEl.createChild({
26084             tag : 'div',
26085             cls : 'roo-document-manager-loading',
26086             cn : [
26087                 {
26088                     tag : 'div',
26089                     tooltip : file.name,
26090                     cls : 'roo-document-manager-thumb',
26091                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26092                 }
26093             ]
26094
26095         });
26096
26097         this.xhr.open(this.method, this.url, true);
26098         
26099         var headers = {
26100             "Accept": "application/json",
26101             "Cache-Control": "no-cache",
26102             "X-Requested-With": "XMLHttpRequest"
26103         };
26104         
26105         for (var headerName in headers) {
26106             var headerValue = headers[headerName];
26107             if (headerValue) {
26108                 this.xhr.setRequestHeader(headerName, headerValue);
26109             }
26110         }
26111         
26112         var _this = this;
26113         
26114         this.xhr.onload = function()
26115         {
26116             _this.xhrOnLoad(_this.xhr);
26117         }
26118         
26119         this.xhr.onerror = function()
26120         {
26121             _this.xhrOnError(_this.xhr);
26122         }
26123         
26124         var formData = new FormData();
26125
26126         formData.append('returnHTML', 'NO');
26127         
26128         if(crop){
26129             formData.append('crop', crop);
26130         }
26131         
26132         formData.append(this.paramName, file, file.name);
26133         
26134         if(this.fireEvent('prepare', this, formData) != false){
26135             this.xhr.send(formData);
26136         };
26137     },
26138     
26139     uploadCancel : function()
26140     {
26141         this.xhr.abort();
26142         
26143         this.delegates = [];
26144         
26145         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26146             el.remove();
26147         }, this);
26148         
26149         this.arrange();
26150     },
26151     
26152     renderPreview : function(file)
26153     {
26154         if(typeof(file.target) != 'undefined' && file.target){
26155             return file;
26156         }
26157         
26158         var previewEl = this.managerEl.createChild({
26159             tag : 'div',
26160             cls : 'roo-document-manager-preview',
26161             cn : [
26162                 {
26163                     tag : 'div',
26164                     tooltip : file.filename,
26165                     cls : 'roo-document-manager-thumb',
26166                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26167                 },
26168                 {
26169                     tag : 'button',
26170                     cls : 'close',
26171                     html : '<i class="fa fa-times-circle"></i>'
26172                 }
26173             ]
26174         });
26175
26176         var close = previewEl.select('button.close', true).first();
26177
26178         close.on('click', this.onRemove, this, file);
26179
26180         file.target = previewEl;
26181
26182         var image = previewEl.select('img', true).first();
26183         
26184         var _this = this;
26185         
26186         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26187         
26188         image.on('click', this.onClick, this, file);
26189         
26190         return file;
26191         
26192     },
26193     
26194     onPreviewLoad : function(file, image)
26195     {
26196         if(typeof(file.target) == 'undefined' || !file.target){
26197             return;
26198         }
26199         
26200         var width = image.dom.naturalWidth || image.dom.width;
26201         var height = image.dom.naturalHeight || image.dom.height;
26202         
26203         if(width > height){
26204             file.target.addClass('wide');
26205             return;
26206         }
26207         
26208         file.target.addClass('tall');
26209         return;
26210         
26211     }
26212 });
26213
26214 /*
26215 * Licence: LGPL
26216 */
26217
26218 /**
26219  * @class Roo.bootstrap.DocumentViewer
26220  * @extends Roo.bootstrap.Component
26221  * Bootstrap DocumentViewer class
26222  * 
26223  * @constructor
26224  * Create a new DocumentViewer
26225  * @param {Object} config The config object
26226  */
26227
26228 Roo.bootstrap.DocumentViewer = function(config){
26229     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26230     
26231     this.addEvents({
26232         /**
26233          * @event initial
26234          * Fire after initEvent
26235          * @param {Roo.bootstrap.DocumentViewer} this
26236          */
26237         "initial" : true,
26238         /**
26239          * @event click
26240          * Fire after click
26241          * @param {Roo.bootstrap.DocumentViewer} this
26242          */
26243         "click" : true,
26244         /**
26245          * @event trash
26246          * Fire after trash button
26247          * @param {Roo.bootstrap.DocumentViewer} this
26248          */
26249         "trash" : true
26250         
26251     });
26252 };
26253
26254 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26255     
26256     getAutoCreate : function()
26257     {
26258         var cfg = {
26259             tag : 'div',
26260             cls : 'roo-document-viewer',
26261             cn : [
26262                 {
26263                     tag : 'div',
26264                     cls : 'roo-document-viewer-body',
26265                     cn : [
26266                         {
26267                             tag : 'div',
26268                             cls : 'roo-document-viewer-thumb',
26269                             cn : [
26270                                 {
26271                                     tag : 'img',
26272                                     cls : 'roo-document-viewer-image'
26273                                 }
26274                             ]
26275                         }
26276                     ]
26277                 },
26278                 {
26279                     tag : 'div',
26280                     cls : 'roo-document-viewer-footer',
26281                     cn : {
26282                         tag : 'div',
26283                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26284                         cn : [
26285                             {
26286                                 tag : 'div',
26287                                 cls : 'btn-group',
26288                                 cn : [
26289                                     {
26290                                         tag : 'button',
26291                                         cls : 'btn btn-default roo-document-viewer-trash',
26292                                         html : '<i class="fa fa-trash"></i>'
26293                                     }
26294                                 ]
26295                             }
26296                         ]
26297                     }
26298                 }
26299             ]
26300         };
26301         
26302         return cfg;
26303     },
26304     
26305     initEvents : function()
26306     {
26307         
26308         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26309         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26310         
26311         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26312         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26313         
26314         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26315         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26316         
26317         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26318         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26319         
26320         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26321         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26322         
26323         this.bodyEl.on('click', this.onClick, this);
26324         
26325         this.trashBtn.on('click', this.onTrash, this);
26326         
26327     },
26328     
26329     initial : function()
26330     {
26331 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26332         
26333         
26334         this.fireEvent('initial', this);
26335         
26336     },
26337     
26338     onClick : function(e)
26339     {
26340         e.preventDefault();
26341         
26342         this.fireEvent('click', this);
26343     },
26344     
26345     onTrash : function(e)
26346     {
26347         e.preventDefault();
26348         
26349         this.fireEvent('trash', this);
26350     }
26351     
26352 });
26353 /*
26354  * - LGPL
26355  *
26356  * nav progress bar
26357  * 
26358  */
26359
26360 /**
26361  * @class Roo.bootstrap.NavProgressBar
26362  * @extends Roo.bootstrap.Component
26363  * Bootstrap NavProgressBar class
26364  * 
26365  * @constructor
26366  * Create a new nav progress bar
26367  * @param {Object} config The config object
26368  */
26369
26370 Roo.bootstrap.NavProgressBar = function(config){
26371     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26372
26373     this.bullets = this.bullets || [];
26374    
26375 //    Roo.bootstrap.NavProgressBar.register(this);
26376      this.addEvents({
26377         /**
26378              * @event changed
26379              * Fires when the active item changes
26380              * @param {Roo.bootstrap.NavProgressBar} this
26381              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26382              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26383          */
26384         'changed': true
26385      });
26386     
26387 };
26388
26389 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26390     
26391     bullets : [],
26392     barItems : [],
26393     
26394     
26395     getAutoCreate : function()
26396     {
26397         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26398         
26399         cfg = {
26400             tag : 'ul',
26401             cls: 'roo-navigation-bar' 
26402         }
26403         
26404         return cfg;
26405         
26406     },
26407     
26408     onRender : function(ct, position) 
26409     {
26410         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26411         
26412         if(this.bullets.length){
26413             Roo.each(this.bullets, function(b){
26414                this.addItem(b);
26415             }, this);
26416         }
26417         
26418     },
26419     
26420     addItem : function(cfg)
26421     {
26422         var item = new Roo.bootstrap.NavProgressItem(cfg);
26423         
26424         item.parentId = this.id;
26425         item.onRender(this.el, null);
26426         
26427         this.barItems.push(item);
26428         
26429         this.formatBullets();
26430         
26431         return item;
26432     },
26433     
26434     getActive : function()
26435     {
26436         var active = false;
26437         
26438         Roo.each(this.barItems, function(v){
26439             
26440             if (!v.isActive()) {
26441                 return;
26442             }
26443             
26444             active = v;
26445             return false;
26446             
26447         });
26448         
26449         return active;
26450     },
26451     
26452     setActiveItem : function(item)
26453     {
26454         var prev = false;
26455         
26456         Roo.each(this.barItems, function(v){
26457             if (v.rid == item.rid) {
26458                 return ;
26459             }
26460             
26461             if (v.isActive()) {
26462                 v.setActive(false);
26463                 prev = v;
26464             }
26465         });
26466
26467         item.setActive(true);
26468         
26469         this.fireEvent('changed', this, item, prev);
26470     },
26471     
26472     getBarItem: function(rid)
26473     {
26474         var ret = false;
26475         
26476         Roo.each(this.barItems, function(e) {
26477             if (e.rid != rid) {
26478                 return;
26479             }
26480             
26481             ret =  e;
26482             return false;
26483         });
26484         
26485         return ret;
26486     },
26487     
26488     indexOfItem : function(item)
26489     {
26490         var index = false;
26491         
26492         Roo.each(this.barItems, function(v, i){
26493             
26494             if (v.rid != item.rid) {
26495                 return;
26496             }
26497             
26498             index = i;
26499             return false
26500         });
26501         
26502         return index;
26503     },
26504     
26505     setActiveNext : function()
26506     {
26507         var i = this.indexOfItem(this.getActive());
26508         
26509         if (i > this.barItems.length) {
26510             return;
26511         }
26512         
26513         this.setActiveItem(this.barItems[i+1]);
26514     },
26515     
26516     setActivePrev : function()
26517     {
26518         var i = this.indexOfItem(this.getActive());
26519         
26520         if (i  < 1) {
26521             return;
26522         }
26523         
26524         this.setActiveItem(this.barItems[i-1]);
26525     },
26526     
26527     formatBullets : function()
26528     {
26529         if(!this.barItems.length){
26530             return;
26531         }
26532         
26533         var width = 100 / this.barItems.length;
26534         
26535         Roo.each(this.barItems, function(i){
26536             i.el.setStyle('width', width + '%');
26537         }, this);
26538     }
26539     
26540 });
26541 /*
26542  * - LGPL
26543  *
26544  * Nav Progress Item
26545  * 
26546  */
26547
26548 /**
26549  * @class Roo.bootstrap.NavProgressItem
26550  * @extends Roo.bootstrap.Component
26551  * Bootstrap NavProgressItem class
26552  * @cfg {String} rid the reference id
26553  * @cfg {Boolean} active (true|false) Is item active default false
26554  * @cfg {Boolean} disabled (true|false) Is item active default false
26555  * @cfg {String} html
26556  * @cfg {String} position (top|bottom) text position default bottom
26557  * @cfg {String} icon show icon instead of number
26558  * @cfg {Boolean} forceIcon (true|false) true to force show icon..if set to false, Roo.isTouch showing icon, otherwish number
26559  * 
26560  * @constructor
26561  * Create a new NavProgressItem
26562  * @param {Object} config The config object
26563  */
26564 Roo.bootstrap.NavProgressItem = function(config){
26565     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
26566     this.addEvents({
26567         // raw events
26568         /**
26569          * @event click
26570          * The raw click event for the entire grid.
26571          * @param {Roo.bootstrap.NavProgressItem} this
26572          * @param {Roo.EventObject} e
26573          */
26574         "click" : true
26575     });
26576    
26577 };
26578
26579 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
26580     
26581     rid : '',
26582     active : false,
26583     disabled : false,
26584     html : '',
26585     position : 'bottom',
26586     icon : false,
26587     forceIcon : false,
26588     
26589     getAutoCreate : function()
26590     {
26591         var iconCls = 'roo-navigation-bar-item-icon';
26592         
26593         if((this.forceIcon && this.icon) || !this.forceIcon && Roo.isTouch){
26594             iconCls += ' ' + this.icon;
26595         }
26596         
26597         var cfg = {
26598             tag: 'li',
26599             cls: 'roo-navigation-bar-item',
26600             cn : [
26601                 {
26602                     tag : 'i',
26603                     cls : iconCls
26604                 },
26605                 {
26606                     tag : 'span',
26607                     cls : 'roo-navigation-bar-item-text ' + this.position,
26608                     html : this.html
26609                 }
26610             ]
26611         }
26612         
26613         if(this.active){
26614             cfg.cls += ' active';
26615         }
26616         if(this.disabled){
26617             cfg.cls += ' disabled';
26618         }
26619         
26620         return cfg;
26621     },
26622     
26623     initEvents: function() 
26624     {
26625         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
26626         this.textEl = this.el.select('.roo-navigation-bar-item-text', true).first();
26627         
26628         if(Roo.isTouch){
26629             this.textEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
26630         }
26631         
26632         this.iconEl.on('click', this.onClick, this);
26633         
26634     },
26635     
26636     onClick : function(e)
26637     {
26638         e.preventDefault();
26639         
26640         if(this.disabled){
26641             return;
26642         }
26643         
26644         if(this.fireEvent('click', this, e) === false){
26645             return;
26646         };
26647         
26648         this.parent().setActiveItem(this);
26649     },
26650     
26651     isActive: function () 
26652     {
26653         return this.active;
26654     },
26655     
26656     setActive : function(state)
26657     {
26658         if(this.active == state){
26659             return;
26660         }
26661         
26662         this.active = state;
26663         
26664         if (state) {
26665             this.el.addClass('active');
26666             return;
26667         }
26668         
26669         this.el.removeClass('active');
26670         
26671         return;
26672     },
26673     
26674     setDisabled : function(state)
26675     {
26676         if(this.disabled == state){
26677             return;
26678         }
26679         
26680         this.disabled = state;
26681         
26682         if (state) {
26683             this.el.addClass('disabled');
26684             return;
26685         }
26686         
26687         this.el.removeClass('disabled');
26688     },
26689     
26690     tooltipEl : function()
26691     {
26692         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
26693     }
26694 });
26695  
26696
26697