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} tfoot (true|false) generate tfoot, default true
5389  * @cfg {Boolean} thead (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     if (this.sm) {
5404         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5405         this.sm = this.selModel;
5406         this.sm.xmodule = this.xmodule || false;
5407     }
5408     if (this.cm && typeof(this.cm.config) == 'undefined') {
5409         this.colModel = new Roo.grid.ColumnModel(this.cm);
5410         this.cm = this.colModel;
5411         this.cm.xmodule = this.xmodule || false;
5412     }
5413     if (this.store) {
5414         this.store= Roo.factory(this.store, Roo.data);
5415         this.ds = this.store;
5416         this.ds.xmodule = this.xmodule || false;
5417          
5418     }
5419     if (this.footer && this.store) {
5420         this.footer.dataSource = this.ds;
5421         this.footer = Roo.factory(this.footer);
5422     }
5423     
5424     /** @private */
5425     this.addEvents({
5426         /**
5427          * @event cellclick
5428          * Fires when a cell is clicked
5429          * @param {Roo.bootstrap.Table} this
5430          * @param {Roo.Element} el
5431          * @param {Number} rowIndex
5432          * @param {Number} columnIndex
5433          * @param {Roo.EventObject} e
5434          */
5435         "cellclick" : true,
5436         /**
5437          * @event celldblclick
5438          * Fires when a cell is double clicked
5439          * @param {Roo.bootstrap.Table} this
5440          * @param {Roo.Element} el
5441          * @param {Number} rowIndex
5442          * @param {Number} columnIndex
5443          * @param {Roo.EventObject} e
5444          */
5445         "celldblclick" : true,
5446         /**
5447          * @event rowclick
5448          * Fires when a row is clicked
5449          * @param {Roo.bootstrap.Table} this
5450          * @param {Roo.Element} el
5451          * @param {Number} rowIndex
5452          * @param {Roo.EventObject} e
5453          */
5454         "rowclick" : true,
5455         /**
5456          * @event rowdblclick
5457          * Fires when a row is double clicked
5458          * @param {Roo.bootstrap.Table} this
5459          * @param {Roo.Element} el
5460          * @param {Number} rowIndex
5461          * @param {Roo.EventObject} e
5462          */
5463         "rowdblclick" : true,
5464         /**
5465          * @event mouseover
5466          * Fires when a mouseover occur
5467          * @param {Roo.bootstrap.Table} this
5468          * @param {Roo.Element} el
5469          * @param {Number} rowIndex
5470          * @param {Number} columnIndex
5471          * @param {Roo.EventObject} e
5472          */
5473         "mouseover" : true,
5474         /**
5475          * @event mouseout
5476          * Fires when a mouseout occur
5477          * @param {Roo.bootstrap.Table} this
5478          * @param {Roo.Element} el
5479          * @param {Number} rowIndex
5480          * @param {Number} columnIndex
5481          * @param {Roo.EventObject} e
5482          */
5483         "mouseout" : true,
5484         /**
5485          * @event rowclass
5486          * Fires when a row is rendered, so you can change add a style to it.
5487          * @param {Roo.bootstrap.Table} this
5488          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5489          */
5490         'rowclass' : true,
5491           /**
5492          * @event rowsrendered
5493          * Fires when all the  rows have been rendered
5494          * @param {Roo.bootstrap.Table} this
5495          */
5496         'rowsrendered' : true
5497         
5498     });
5499 };
5500
5501 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5502     
5503     cls: false,
5504     align: false,
5505     bgcolor: false,
5506     border: false,
5507     cellpadding: false,
5508     cellspacing: false,
5509     frame: false,
5510     rules: false,
5511     sortable: false,
5512     summary: false,
5513     width: false,
5514     striped : false,
5515     bordered: false,
5516     hover:  false,
5517     condensed : false,
5518     responsive : false,
5519     sm : false,
5520     cm : false,
5521     store : false,
5522     loadMask : false,
5523     tfoot : true,
5524     thead : true,
5525     RowSelection : false,
5526     CellSelection : false,
5527     layout : false,
5528     
5529     // Roo.Element - the tbody
5530     mainBody: false, 
5531     
5532     getAutoCreate : function(){
5533         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5534         
5535         cfg = {
5536             tag: 'table',
5537             cls : 'table',
5538             cn : []
5539         }
5540             
5541         if (this.striped) {
5542             cfg.cls += ' table-striped';
5543         }
5544         
5545         if (this.hover) {
5546             cfg.cls += ' table-hover';
5547         }
5548         if (this.bordered) {
5549             cfg.cls += ' table-bordered';
5550         }
5551         if (this.condensed) {
5552             cfg.cls += ' table-condensed';
5553         }
5554         if (this.responsive) {
5555             cfg.cls += ' table-responsive';
5556         }
5557         
5558         if (this.cls) {
5559             cfg.cls+=  ' ' +this.cls;
5560         }
5561         
5562         // this lot should be simplifed...
5563         
5564         if (this.align) {
5565             cfg.align=this.align;
5566         }
5567         if (this.bgcolor) {
5568             cfg.bgcolor=this.bgcolor;
5569         }
5570         if (this.border) {
5571             cfg.border=this.border;
5572         }
5573         if (this.cellpadding) {
5574             cfg.cellpadding=this.cellpadding;
5575         }
5576         if (this.cellspacing) {
5577             cfg.cellspacing=this.cellspacing;
5578         }
5579         if (this.frame) {
5580             cfg.frame=this.frame;
5581         }
5582         if (this.rules) {
5583             cfg.rules=this.rules;
5584         }
5585         if (this.sortable) {
5586             cfg.sortable=this.sortable;
5587         }
5588         if (this.summary) {
5589             cfg.summary=this.summary;
5590         }
5591         if (this.width) {
5592             cfg.width=this.width;
5593         }
5594         if (this.layout) {
5595             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5596         }
5597         
5598         if(this.store || this.cm){
5599             if(this.thead){
5600                 cfg.cn.push(this.renderHeader());
5601             }
5602             
5603             cfg.cn.push(this.renderBody());
5604             
5605             if(this.tfoot){
5606                 cfg.cn.push(this.renderFooter());
5607             }
5608             
5609             cfg.cls+=  ' TableGrid';
5610         }
5611         
5612         return { cn : [ cfg ] };
5613     },
5614     
5615     initEvents : function()
5616     {   
5617         if(!this.store || !this.cm){
5618             return;
5619         }
5620         
5621         //Roo.log('initEvents with ds!!!!');
5622         
5623         this.mainBody = this.el.select('tbody', true).first();
5624         
5625         
5626         var _this = this;
5627         
5628         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5629             e.on('click', _this.sort, _this);
5630         });
5631         
5632         this.el.on("click", this.onClick, this);
5633         this.el.on("dblclick", this.onDblClick, this);
5634         
5635         // why is this done????? = it breaks dialogs??
5636         //this.parent().el.setStyle('position', 'relative');
5637         
5638         
5639         if (this.footer) {
5640             this.footer.parentId = this.id;
5641             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5642         }
5643         
5644         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5645         
5646         this.store.on('load', this.onLoad, this);
5647         this.store.on('beforeload', this.onBeforeLoad, this);
5648         this.store.on('update', this.onUpdate, this);
5649         this.store.on('add', this.onAdd, this);
5650         
5651     },
5652     
5653     onMouseover : function(e, el)
5654     {
5655         var cell = Roo.get(el);
5656         
5657         if(!cell){
5658             return;
5659         }
5660         
5661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5662             cell = cell.findParent('td', false, true);
5663         }
5664         
5665         var row = cell.findParent('tr', false, true);
5666         var cellIndex = cell.dom.cellIndex;
5667         var rowIndex = row.dom.rowIndex - 1; // start from 0
5668         
5669         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5670         
5671     },
5672     
5673     onMouseout : function(e, el)
5674     {
5675         var cell = Roo.get(el);
5676         
5677         if(!cell){
5678             return;
5679         }
5680         
5681         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5682             cell = cell.findParent('td', false, true);
5683         }
5684         
5685         var row = cell.findParent('tr', false, true);
5686         var cellIndex = cell.dom.cellIndex;
5687         var rowIndex = row.dom.rowIndex - 1; // start from 0
5688         
5689         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5690         
5691     },
5692     
5693     onClick : function(e, el)
5694     {
5695         var cell = Roo.get(el);
5696         
5697         if(!cell || (!this.CellSelection && !this.RowSelection)){
5698             return;
5699         }
5700         
5701         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5702             cell = cell.findParent('td', false, true);
5703         }
5704         
5705         if(!cell || typeof(cell) == 'undefined'){
5706             return;
5707         }
5708         
5709         var row = cell.findParent('tr', false, true);
5710         
5711         if(!row || typeof(row) == 'undefined'){
5712             return;
5713         }
5714         
5715         var cellIndex = cell.dom.cellIndex;
5716         var rowIndex = this.getRowIndex(row);
5717         
5718         if(this.CellSelection){
5719             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5720         }
5721         
5722         if(this.RowSelection){
5723             this.fireEvent('rowclick', this, row, rowIndex, e);
5724         }
5725         
5726         
5727     },
5728     
5729     onDblClick : function(e,el)
5730     {
5731         var cell = Roo.get(el);
5732         
5733         if(!cell || (!this.CellSelection && !this.RowSelection)){
5734             return;
5735         }
5736         
5737         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5738             cell = cell.findParent('td', false, true);
5739         }
5740         
5741         if(!cell || typeof(cell) == 'undefined'){
5742             return;
5743         }
5744         
5745         var row = cell.findParent('tr', false, true);
5746         
5747         if(!row || typeof(row) == 'undefined'){
5748             return;
5749         }
5750         
5751         var cellIndex = cell.dom.cellIndex;
5752         var rowIndex = this.getRowIndex(row);
5753         
5754         if(this.CellSelection){
5755             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5756         }
5757         
5758         if(this.RowSelection){
5759             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5760         }
5761     },
5762     
5763     sort : function(e,el)
5764     {
5765         var col = Roo.get(el);
5766         
5767         if(!col.hasClass('sortable')){
5768             return;
5769         }
5770         
5771         var sort = col.attr('sort');
5772         var dir = 'ASC';
5773         
5774         if(col.hasClass('glyphicon-arrow-up')){
5775             dir = 'DESC';
5776         }
5777         
5778         this.store.sortInfo = {field : sort, direction : dir};
5779         
5780         if (this.footer) {
5781             Roo.log("calling footer first");
5782             this.footer.onClick('first');
5783         } else {
5784         
5785             this.store.load({ params : { start : 0 } });
5786         }
5787     },
5788     
5789     renderHeader : function()
5790     {
5791         var header = {
5792             tag: 'thead',
5793             cn : []
5794         };
5795         
5796         var cm = this.cm;
5797         
5798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5799             
5800             var config = cm.config[i];
5801                     
5802             var c = {
5803                 tag: 'th',
5804                 style : '',
5805                 html: cm.getColumnHeader(i)
5806             };
5807             
5808             if(typeof(config.tooltip) != 'undefined'){
5809                 c.tooltip = config.tooltip;
5810             }
5811             
5812             if(typeof(config.colspan) != 'undefined'){
5813                 c.colspan = config.colspan;
5814             }
5815             
5816             if(typeof(config.hidden) != 'undefined' && config.hidden){
5817                 c.style += ' display:none;';
5818             }
5819             
5820             if(typeof(config.dataIndex) != 'undefined'){
5821                 c.sort = config.dataIndex;
5822             }
5823             
5824             if(typeof(config.sortable) != 'undefined' && config.sortable){
5825                 c.cls = 'sortable';
5826             }
5827             
5828             if(typeof(config.align) != 'undefined' && config.align.length){
5829                 c.style += ' text-align:' + config.align + ';';
5830             }
5831             
5832             if(typeof(config.width) != 'undefined'){
5833                 c.style += ' width:' + config.width + 'px;';
5834             }
5835             
5836             if(typeof(config.cls) != 'undefined'){
5837                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5838             }
5839             
5840             header.cn.push(c)
5841         }
5842         
5843         return header;
5844     },
5845     
5846     renderBody : function()
5847     {
5848         var body = {
5849             tag: 'tbody',
5850             cn : [
5851                 {
5852                     tag: 'tr',
5853                     cn : [
5854                         {
5855                             tag : 'td',
5856                             colspan :  this.cm.getColumnCount()
5857                         }
5858                     ]
5859                 }
5860             ]
5861         };
5862         
5863         return body;
5864     },
5865     
5866     renderFooter : function()
5867     {
5868         var footer = {
5869             tag: 'tfoot',
5870             cn : [
5871                 {
5872                     tag: 'tr',
5873                     cn : [
5874                         {
5875                             tag : 'td',
5876                             colspan :  this.cm.getColumnCount()
5877                         }
5878                     ]
5879                 }
5880             ]
5881         };
5882         
5883         return footer;
5884     },
5885     
5886     
5887     
5888     onLoad : function()
5889     {
5890         Roo.log('ds onload');
5891         this.clear();
5892         
5893         var _this = this;
5894         var cm = this.cm;
5895         var ds = this.store;
5896         
5897         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5898             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5899             
5900             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5901                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5902             }
5903             
5904             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5905                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5906             }
5907         });
5908         
5909         var tbody =  this.mainBody;
5910               
5911         if(ds.getCount() > 0){
5912             ds.data.each(function(d,rowIndex){
5913                 var row =  this.renderRow(cm, ds, rowIndex);
5914                 
5915                 tbody.createChild(row);
5916                 
5917                 var _this = this;
5918                 
5919                 if(row.cellObjects.length){
5920                     Roo.each(row.cellObjects, function(r){
5921                         _this.renderCellObject(r);
5922                     })
5923                 }
5924                 
5925             }, this);
5926         }
5927         
5928         Roo.each(this.el.select('tbody td', true).elements, function(e){
5929             e.on('mouseover', _this.onMouseover, _this);
5930         });
5931         
5932         Roo.each(this.el.select('tbody td', true).elements, function(e){
5933             e.on('mouseout', _this.onMouseout, _this);
5934         });
5935         this.fireEvent('rowsrendered', this);
5936         //if(this.loadMask){
5937         //    this.maskEl.hide();
5938         //}
5939     },
5940     
5941     
5942     onUpdate : function(ds,record)
5943     {
5944         this.refreshRow(record);
5945     },
5946     
5947     onRemove : function(ds, record, index, isUpdate){
5948         if(isUpdate !== true){
5949             this.fireEvent("beforerowremoved", this, index, record);
5950         }
5951         var bt = this.mainBody.dom;
5952         
5953         var rows = this.el.select('tbody > tr', true).elements;
5954         
5955         if(typeof(rows[index]) != 'undefined'){
5956             bt.removeChild(rows[index].dom);
5957         }
5958         
5959 //        if(bt.rows[index]){
5960 //            bt.removeChild(bt.rows[index]);
5961 //        }
5962         
5963         if(isUpdate !== true){
5964             //this.stripeRows(index);
5965             //this.syncRowHeights(index, index);
5966             //this.layout();
5967             this.fireEvent("rowremoved", this, index, record);
5968         }
5969     },
5970     
5971     onAdd : function(ds, records, rowIndex)
5972     {
5973         //Roo.log('on Add called');
5974         // - note this does not handle multiple adding very well..
5975         var bt = this.mainBody.dom;
5976         for (var i =0 ; i < records.length;i++) {
5977             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5978             //Roo.log(records[i]);
5979             //Roo.log(this.store.getAt(rowIndex+i));
5980             this.insertRow(this.store, rowIndex + i, false);
5981             return;
5982         }
5983         
5984     },
5985     
5986     
5987     refreshRow : function(record){
5988         var ds = this.store, index;
5989         if(typeof record == 'number'){
5990             index = record;
5991             record = ds.getAt(index);
5992         }else{
5993             index = ds.indexOf(record);
5994         }
5995         this.insertRow(ds, index, true);
5996         this.onRemove(ds, record, index+1, true);
5997         //this.syncRowHeights(index, index);
5998         //this.layout();
5999         this.fireEvent("rowupdated", this, index, record);
6000     },
6001     
6002     insertRow : function(dm, rowIndex, isUpdate){
6003         
6004         if(!isUpdate){
6005             this.fireEvent("beforerowsinserted", this, rowIndex);
6006         }
6007             //var s = this.getScrollState();
6008         var row = this.renderRow(this.cm, this.store, rowIndex);
6009         // insert before rowIndex..
6010         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6011         
6012         var _this = this;
6013                 
6014         if(row.cellObjects.length){
6015             Roo.each(row.cellObjects, function(r){
6016                 _this.renderCellObject(r);
6017             })
6018         }
6019             
6020         if(!isUpdate){
6021             this.fireEvent("rowsinserted", this, rowIndex);
6022             //this.syncRowHeights(firstRow, lastRow);
6023             //this.stripeRows(firstRow);
6024             //this.layout();
6025         }
6026         
6027     },
6028     
6029     
6030     getRowDom : function(rowIndex)
6031     {
6032         var rows = this.el.select('tbody > tr', true).elements;
6033         
6034         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6035         
6036     },
6037     // returns the object tree for a tr..
6038   
6039     
6040     renderRow : function(cm, ds, rowIndex) 
6041     {
6042         
6043         var d = ds.getAt(rowIndex);
6044         
6045         var row = {
6046             tag : 'tr',
6047             cn : []
6048         };
6049             
6050         var cellObjects = [];
6051         
6052         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6053             var config = cm.config[i];
6054             
6055             var renderer = cm.getRenderer(i);
6056             var value = '';
6057             var id = false;
6058             
6059             if(typeof(renderer) !== 'undefined'){
6060                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6061             }
6062             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6063             // and are rendered into the cells after the row is rendered - using the id for the element.
6064             
6065             if(typeof(value) === 'object'){
6066                 id = Roo.id();
6067                 cellObjects.push({
6068                     container : id,
6069                     cfg : value 
6070                 })
6071             }
6072             
6073             var rowcfg = {
6074                 record: d,
6075                 rowIndex : rowIndex,
6076                 colIndex : i,
6077                 rowClass : ''
6078             }
6079
6080             this.fireEvent('rowclass', this, rowcfg);
6081             
6082             var td = {
6083                 tag: 'td',
6084                 cls : rowcfg.rowClass,
6085                 style: '',
6086                 html: (typeof(value) === 'object') ? '' : value
6087             };
6088             
6089             if (id) {
6090                 td.id = id;
6091             }
6092             
6093             if(typeof(config.colspan) != 'undefined'){
6094                 td.colspan = config.colspan;
6095             }
6096             
6097             if(typeof(config.hidden) != 'undefined' && config.hidden){
6098                 td.style += ' display:none;';
6099             }
6100             
6101             if(typeof(config.align) != 'undefined' && config.align.length){
6102                 td.style += ' text-align:' + config.align + ';';
6103             }
6104             
6105             if(typeof(config.width) != 'undefined'){
6106                 td.style += ' width:' +  config.width + 'px;';
6107             }
6108             
6109             if(typeof(config.cursor) != 'undefined'){
6110                 td.style += ' cursor:' +  config.cursor + ';';
6111             }
6112             
6113             if(typeof(config.cls) != 'undefined'){
6114                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6115             }
6116              
6117             row.cn.push(td);
6118            
6119         }
6120         
6121         row.cellObjects = cellObjects;
6122         
6123         return row;
6124           
6125     },
6126     
6127     
6128     
6129     onBeforeLoad : function()
6130     {
6131         //Roo.log('ds onBeforeLoad');
6132         
6133         //this.clear();
6134         
6135         //if(this.loadMask){
6136         //    this.maskEl.show();
6137         //}
6138     },
6139      /**
6140      * Remove all rows
6141      */
6142     clear : function()
6143     {
6144         this.el.select('tbody', true).first().dom.innerHTML = '';
6145     },
6146     /**
6147      * Show or hide a row.
6148      * @param {Number} rowIndex to show or hide
6149      * @param {Boolean} state hide
6150      */
6151     setRowVisibility : function(rowIndex, state)
6152     {
6153         var bt = this.mainBody.dom;
6154         
6155         var rows = this.el.select('tbody > tr', true).elements;
6156         
6157         if(typeof(rows[rowIndex]) == 'undefined'){
6158             return;
6159         }
6160         rows[rowIndex].dom.style.display = state ? '' : 'none';
6161     },
6162     
6163     
6164     getSelectionModel : function(){
6165         if(!this.selModel){
6166             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6167         }
6168         return this.selModel;
6169     },
6170     /*
6171      * Render the Roo.bootstrap object from renderder
6172      */
6173     renderCellObject : function(r)
6174     {
6175         var _this = this;
6176         
6177         var t = r.cfg.render(r.container);
6178         
6179         if(r.cfg.cn){
6180             Roo.each(r.cfg.cn, function(c){
6181                 var child = {
6182                     container: t.getChildContainer(),
6183                     cfg: c
6184                 }
6185                 _this.renderCellObject(child);
6186             })
6187         }
6188     },
6189     
6190     getRowIndex : function(row)
6191     {
6192         var rowIndex = -1;
6193         
6194         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6195             if(el != row){
6196                 return;
6197             }
6198             
6199             rowIndex = index;
6200         });
6201         
6202         return rowIndex;
6203     }
6204    
6205 });
6206
6207  
6208
6209  /*
6210  * - LGPL
6211  *
6212  * table cell
6213  * 
6214  */
6215
6216 /**
6217  * @class Roo.bootstrap.TableCell
6218  * @extends Roo.bootstrap.Component
6219  * Bootstrap TableCell class
6220  * @cfg {String} html cell contain text
6221  * @cfg {String} cls cell class
6222  * @cfg {String} tag cell tag (td|th) default td
6223  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6224  * @cfg {String} align Aligns the content in a cell
6225  * @cfg {String} axis Categorizes cells
6226  * @cfg {String} bgcolor Specifies the background color of a cell
6227  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6228  * @cfg {Number} colspan Specifies the number of columns a cell should span
6229  * @cfg {String} headers Specifies one or more header cells a cell is related to
6230  * @cfg {Number} height Sets the height of a cell
6231  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6232  * @cfg {Number} rowspan Sets the number of rows a cell should span
6233  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6234  * @cfg {String} valign Vertical aligns the content in a cell
6235  * @cfg {Number} width Specifies the width of a cell
6236  * 
6237  * @constructor
6238  * Create a new TableCell
6239  * @param {Object} config The config object
6240  */
6241
6242 Roo.bootstrap.TableCell = function(config){
6243     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6244 };
6245
6246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6247     
6248     html: false,
6249     cls: false,
6250     tag: false,
6251     abbr: false,
6252     align: false,
6253     axis: false,
6254     bgcolor: false,
6255     charoff: false,
6256     colspan: false,
6257     headers: false,
6258     height: false,
6259     nowrap: false,
6260     rowspan: false,
6261     scope: false,
6262     valign: false,
6263     width: false,
6264     
6265     
6266     getAutoCreate : function(){
6267         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6268         
6269         cfg = {
6270             tag: 'td'
6271         }
6272         
6273         if(this.tag){
6274             cfg.tag = this.tag;
6275         }
6276         
6277         if (this.html) {
6278             cfg.html=this.html
6279         }
6280         if (this.cls) {
6281             cfg.cls=this.cls
6282         }
6283         if (this.abbr) {
6284             cfg.abbr=this.abbr
6285         }
6286         if (this.align) {
6287             cfg.align=this.align
6288         }
6289         if (this.axis) {
6290             cfg.axis=this.axis
6291         }
6292         if (this.bgcolor) {
6293             cfg.bgcolor=this.bgcolor
6294         }
6295         if (this.charoff) {
6296             cfg.charoff=this.charoff
6297         }
6298         if (this.colspan) {
6299             cfg.colspan=this.colspan
6300         }
6301         if (this.headers) {
6302             cfg.headers=this.headers
6303         }
6304         if (this.height) {
6305             cfg.height=this.height
6306         }
6307         if (this.nowrap) {
6308             cfg.nowrap=this.nowrap
6309         }
6310         if (this.rowspan) {
6311             cfg.rowspan=this.rowspan
6312         }
6313         if (this.scope) {
6314             cfg.scope=this.scope
6315         }
6316         if (this.valign) {
6317             cfg.valign=this.valign
6318         }
6319         if (this.width) {
6320             cfg.width=this.width
6321         }
6322         
6323         
6324         return cfg;
6325     }
6326    
6327 });
6328
6329  
6330
6331  /*
6332  * - LGPL
6333  *
6334  * table row
6335  * 
6336  */
6337
6338 /**
6339  * @class Roo.bootstrap.TableRow
6340  * @extends Roo.bootstrap.Component
6341  * Bootstrap TableRow class
6342  * @cfg {String} cls row class
6343  * @cfg {String} align Aligns the content in a table row
6344  * @cfg {String} bgcolor Specifies a background color for a table row
6345  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6346  * @cfg {String} valign Vertical aligns the content in a table row
6347  * 
6348  * @constructor
6349  * Create a new TableRow
6350  * @param {Object} config The config object
6351  */
6352
6353 Roo.bootstrap.TableRow = function(config){
6354     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6355 };
6356
6357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6358     
6359     cls: false,
6360     align: false,
6361     bgcolor: false,
6362     charoff: false,
6363     valign: false,
6364     
6365     getAutoCreate : function(){
6366         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6367         
6368         cfg = {
6369             tag: 'tr'
6370         }
6371             
6372         if(this.cls){
6373             cfg.cls = this.cls;
6374         }
6375         if(this.align){
6376             cfg.align = this.align;
6377         }
6378         if(this.bgcolor){
6379             cfg.bgcolor = this.bgcolor;
6380         }
6381         if(this.charoff){
6382             cfg.charoff = this.charoff;
6383         }
6384         if(this.valign){
6385             cfg.valign = this.valign;
6386         }
6387         
6388         return cfg;
6389     }
6390    
6391 });
6392
6393  
6394
6395  /*
6396  * - LGPL
6397  *
6398  * table body
6399  * 
6400  */
6401
6402 /**
6403  * @class Roo.bootstrap.TableBody
6404  * @extends Roo.bootstrap.Component
6405  * Bootstrap TableBody class
6406  * @cfg {String} cls element class
6407  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6408  * @cfg {String} align Aligns the content inside the element
6409  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6410  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6411  * 
6412  * @constructor
6413  * Create a new TableBody
6414  * @param {Object} config The config object
6415  */
6416
6417 Roo.bootstrap.TableBody = function(config){
6418     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6419 };
6420
6421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6422     
6423     cls: false,
6424     tag: false,
6425     align: false,
6426     charoff: false,
6427     valign: false,
6428     
6429     getAutoCreate : function(){
6430         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6431         
6432         cfg = {
6433             tag: 'tbody'
6434         }
6435             
6436         if (this.cls) {
6437             cfg.cls=this.cls
6438         }
6439         if(this.tag){
6440             cfg.tag = this.tag;
6441         }
6442         
6443         if(this.align){
6444             cfg.align = this.align;
6445         }
6446         if(this.charoff){
6447             cfg.charoff = this.charoff;
6448         }
6449         if(this.valign){
6450             cfg.valign = this.valign;
6451         }
6452         
6453         return cfg;
6454     }
6455     
6456     
6457 //    initEvents : function()
6458 //    {
6459 //        
6460 //        if(!this.store){
6461 //            return;
6462 //        }
6463 //        
6464 //        this.store = Roo.factory(this.store, Roo.data);
6465 //        this.store.on('load', this.onLoad, this);
6466 //        
6467 //        this.store.load();
6468 //        
6469 //    },
6470 //    
6471 //    onLoad: function () 
6472 //    {   
6473 //        this.fireEvent('load', this);
6474 //    }
6475 //    
6476 //   
6477 });
6478
6479  
6480
6481  /*
6482  * Based on:
6483  * Ext JS Library 1.1.1
6484  * Copyright(c) 2006-2007, Ext JS, LLC.
6485  *
6486  * Originally Released Under LGPL - original licence link has changed is not relivant.
6487  *
6488  * Fork - LGPL
6489  * <script type="text/javascript">
6490  */
6491
6492 // as we use this in bootstrap.
6493 Roo.namespace('Roo.form');
6494  /**
6495  * @class Roo.form.Action
6496  * Internal Class used to handle form actions
6497  * @constructor
6498  * @param {Roo.form.BasicForm} el The form element or its id
6499  * @param {Object} config Configuration options
6500  */
6501
6502  
6503  
6504 // define the action interface
6505 Roo.form.Action = function(form, options){
6506     this.form = form;
6507     this.options = options || {};
6508 };
6509 /**
6510  * Client Validation Failed
6511  * @const 
6512  */
6513 Roo.form.Action.CLIENT_INVALID = 'client';
6514 /**
6515  * Server Validation Failed
6516  * @const 
6517  */
6518 Roo.form.Action.SERVER_INVALID = 'server';
6519  /**
6520  * Connect to Server Failed
6521  * @const 
6522  */
6523 Roo.form.Action.CONNECT_FAILURE = 'connect';
6524 /**
6525  * Reading Data from Server Failed
6526  * @const 
6527  */
6528 Roo.form.Action.LOAD_FAILURE = 'load';
6529
6530 Roo.form.Action.prototype = {
6531     type : 'default',
6532     failureType : undefined,
6533     response : undefined,
6534     result : undefined,
6535
6536     // interface method
6537     run : function(options){
6538
6539     },
6540
6541     // interface method
6542     success : function(response){
6543
6544     },
6545
6546     // interface method
6547     handleResponse : function(response){
6548
6549     },
6550
6551     // default connection failure
6552     failure : function(response){
6553         
6554         this.response = response;
6555         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6556         this.form.afterAction(this, false);
6557     },
6558
6559     processResponse : function(response){
6560         this.response = response;
6561         if(!response.responseText){
6562             return true;
6563         }
6564         this.result = this.handleResponse(response);
6565         return this.result;
6566     },
6567
6568     // utility functions used internally
6569     getUrl : function(appendParams){
6570         var url = this.options.url || this.form.url || this.form.el.dom.action;
6571         if(appendParams){
6572             var p = this.getParams();
6573             if(p){
6574                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6575             }
6576         }
6577         return url;
6578     },
6579
6580     getMethod : function(){
6581         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6582     },
6583
6584     getParams : function(){
6585         var bp = this.form.baseParams;
6586         var p = this.options.params;
6587         if(p){
6588             if(typeof p == "object"){
6589                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6590             }else if(typeof p == 'string' && bp){
6591                 p += '&' + Roo.urlEncode(bp);
6592             }
6593         }else if(bp){
6594             p = Roo.urlEncode(bp);
6595         }
6596         return p;
6597     },
6598
6599     createCallback : function(){
6600         return {
6601             success: this.success,
6602             failure: this.failure,
6603             scope: this,
6604             timeout: (this.form.timeout*1000),
6605             upload: this.form.fileUpload ? this.success : undefined
6606         };
6607     }
6608 };
6609
6610 Roo.form.Action.Submit = function(form, options){
6611     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6612 };
6613
6614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6615     type : 'submit',
6616
6617     haveProgress : false,
6618     uploadComplete : false,
6619     
6620     // uploadProgress indicator.
6621     uploadProgress : function()
6622     {
6623         if (!this.form.progressUrl) {
6624             return;
6625         }
6626         
6627         if (!this.haveProgress) {
6628             Roo.MessageBox.progress("Uploading", "Uploading");
6629         }
6630         if (this.uploadComplete) {
6631            Roo.MessageBox.hide();
6632            return;
6633         }
6634         
6635         this.haveProgress = true;
6636    
6637         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6638         
6639         var c = new Roo.data.Connection();
6640         c.request({
6641             url : this.form.progressUrl,
6642             params: {
6643                 id : uid
6644             },
6645             method: 'GET',
6646             success : function(req){
6647                //console.log(data);
6648                 var rdata = false;
6649                 var edata;
6650                 try  {
6651                    rdata = Roo.decode(req.responseText)
6652                 } catch (e) {
6653                     Roo.log("Invalid data from server..");
6654                     Roo.log(edata);
6655                     return;
6656                 }
6657                 if (!rdata || !rdata.success) {
6658                     Roo.log(rdata);
6659                     Roo.MessageBox.alert(Roo.encode(rdata));
6660                     return;
6661                 }
6662                 var data = rdata.data;
6663                 
6664                 if (this.uploadComplete) {
6665                    Roo.MessageBox.hide();
6666                    return;
6667                 }
6668                    
6669                 if (data){
6670                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6671                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6672                     );
6673                 }
6674                 this.uploadProgress.defer(2000,this);
6675             },
6676        
6677             failure: function(data) {
6678                 Roo.log('progress url failed ');
6679                 Roo.log(data);
6680             },
6681             scope : this
6682         });
6683            
6684     },
6685     
6686     
6687     run : function()
6688     {
6689         // run get Values on the form, so it syncs any secondary forms.
6690         this.form.getValues();
6691         
6692         var o = this.options;
6693         var method = this.getMethod();
6694         var isPost = method == 'POST';
6695         if(o.clientValidation === false || this.form.isValid()){
6696             
6697             if (this.form.progressUrl) {
6698                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6699                     (new Date() * 1) + '' + Math.random());
6700                     
6701             } 
6702             
6703             
6704             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6705                 form:this.form.el.dom,
6706                 url:this.getUrl(!isPost),
6707                 method: method,
6708                 params:isPost ? this.getParams() : null,
6709                 isUpload: this.form.fileUpload
6710             }));
6711             
6712             this.uploadProgress();
6713
6714         }else if (o.clientValidation !== false){ // client validation failed
6715             this.failureType = Roo.form.Action.CLIENT_INVALID;
6716             this.form.afterAction(this, false);
6717         }
6718     },
6719
6720     success : function(response)
6721     {
6722         this.uploadComplete= true;
6723         if (this.haveProgress) {
6724             Roo.MessageBox.hide();
6725         }
6726         
6727         
6728         var result = this.processResponse(response);
6729         if(result === true || result.success){
6730             this.form.afterAction(this, true);
6731             return;
6732         }
6733         if(result.errors){
6734             this.form.markInvalid(result.errors);
6735             this.failureType = Roo.form.Action.SERVER_INVALID;
6736         }
6737         this.form.afterAction(this, false);
6738     },
6739     failure : function(response)
6740     {
6741         this.uploadComplete= true;
6742         if (this.haveProgress) {
6743             Roo.MessageBox.hide();
6744         }
6745         
6746         this.response = response;
6747         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6748         this.form.afterAction(this, false);
6749     },
6750     
6751     handleResponse : function(response){
6752         if(this.form.errorReader){
6753             var rs = this.form.errorReader.read(response);
6754             var errors = [];
6755             if(rs.records){
6756                 for(var i = 0, len = rs.records.length; i < len; i++) {
6757                     var r = rs.records[i];
6758                     errors[i] = r.data;
6759                 }
6760             }
6761             if(errors.length < 1){
6762                 errors = null;
6763             }
6764             return {
6765                 success : rs.success,
6766                 errors : errors
6767             };
6768         }
6769         var ret = false;
6770         try {
6771             ret = Roo.decode(response.responseText);
6772         } catch (e) {
6773             ret = {
6774                 success: false,
6775                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6776                 errors : []
6777             };
6778         }
6779         return ret;
6780         
6781     }
6782 });
6783
6784
6785 Roo.form.Action.Load = function(form, options){
6786     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6787     this.reader = this.form.reader;
6788 };
6789
6790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6791     type : 'load',
6792
6793     run : function(){
6794         
6795         Roo.Ajax.request(Roo.apply(
6796                 this.createCallback(), {
6797                     method:this.getMethod(),
6798                     url:this.getUrl(false),
6799                     params:this.getParams()
6800         }));
6801     },
6802
6803     success : function(response){
6804         
6805         var result = this.processResponse(response);
6806         if(result === true || !result.success || !result.data){
6807             this.failureType = Roo.form.Action.LOAD_FAILURE;
6808             this.form.afterAction(this, false);
6809             return;
6810         }
6811         this.form.clearInvalid();
6812         this.form.setValues(result.data);
6813         this.form.afterAction(this, true);
6814     },
6815
6816     handleResponse : function(response){
6817         if(this.form.reader){
6818             var rs = this.form.reader.read(response);
6819             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6820             return {
6821                 success : rs.success,
6822                 data : data
6823             };
6824         }
6825         return Roo.decode(response.responseText);
6826     }
6827 });
6828
6829 Roo.form.Action.ACTION_TYPES = {
6830     'load' : Roo.form.Action.Load,
6831     'submit' : Roo.form.Action.Submit
6832 };/*
6833  * - LGPL
6834  *
6835  * form
6836  * 
6837  */
6838
6839 /**
6840  * @class Roo.bootstrap.Form
6841  * @extends Roo.bootstrap.Component
6842  * Bootstrap Form class
6843  * @cfg {String} method  GET | POST (default POST)
6844  * @cfg {String} labelAlign top | left (default top)
6845  * @cfg {String} align left  | right - for navbars
6846  * @cfg {Boolean} loadMask load mask when submit (default true)
6847
6848  * 
6849  * @constructor
6850  * Create a new Form
6851  * @param {Object} config The config object
6852  */
6853
6854
6855 Roo.bootstrap.Form = function(config){
6856     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6857     this.addEvents({
6858         /**
6859          * @event clientvalidation
6860          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6861          * @param {Form} this
6862          * @param {Boolean} valid true if the form has passed client-side validation
6863          */
6864         clientvalidation: true,
6865         /**
6866          * @event beforeaction
6867          * Fires before any action is performed. Return false to cancel the action.
6868          * @param {Form} this
6869          * @param {Action} action The action to be performed
6870          */
6871         beforeaction: true,
6872         /**
6873          * @event actionfailed
6874          * Fires when an action fails.
6875          * @param {Form} this
6876          * @param {Action} action The action that failed
6877          */
6878         actionfailed : true,
6879         /**
6880          * @event actioncomplete
6881          * Fires when an action is completed.
6882          * @param {Form} this
6883          * @param {Action} action The action that completed
6884          */
6885         actioncomplete : true
6886     });
6887     
6888 };
6889
6890 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6891       
6892      /**
6893      * @cfg {String} method
6894      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6895      */
6896     method : 'POST',
6897     /**
6898      * @cfg {String} url
6899      * The URL to use for form actions if one isn't supplied in the action options.
6900      */
6901     /**
6902      * @cfg {Boolean} fileUpload
6903      * Set to true if this form is a file upload.
6904      */
6905      
6906     /**
6907      * @cfg {Object} baseParams
6908      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6909      */
6910       
6911     /**
6912      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6913      */
6914     timeout: 30,
6915     /**
6916      * @cfg {Sting} align (left|right) for navbar forms
6917      */
6918     align : 'left',
6919
6920     // private
6921     activeAction : null,
6922  
6923     /**
6924      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6925      * element by passing it or its id or mask the form itself by passing in true.
6926      * @type Mixed
6927      */
6928     waitMsgTarget : false,
6929     
6930     loadMask : true,
6931     
6932     getAutoCreate : function(){
6933         
6934         var cfg = {
6935             tag: 'form',
6936             method : this.method || 'POST',
6937             id : this.id || Roo.id(),
6938             cls : ''
6939         }
6940         if (this.parent().xtype.match(/^Nav/)) {
6941             cfg.cls = 'navbar-form navbar-' + this.align;
6942             
6943         }
6944         
6945         if (this.labelAlign == 'left' ) {
6946             cfg.cls += ' form-horizontal';
6947         }
6948         
6949         
6950         return cfg;
6951     },
6952     initEvents : function()
6953     {
6954         this.el.on('submit', this.onSubmit, this);
6955         // this was added as random key presses on the form where triggering form submit.
6956         this.el.on('keypress', function(e) {
6957             if (e.getCharCode() != 13) {
6958                 return true;
6959             }
6960             // we might need to allow it for textareas.. and some other items.
6961             // check e.getTarget().
6962             
6963             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6964                 return true;
6965             }
6966         
6967             Roo.log("keypress blocked");
6968             
6969             e.preventDefault();
6970             return false;
6971         });
6972         
6973     },
6974     // private
6975     onSubmit : function(e){
6976         e.stopEvent();
6977     },
6978     
6979      /**
6980      * Returns true if client-side validation on the form is successful.
6981      * @return Boolean
6982      */
6983     isValid : function(){
6984         var items = this.getItems();
6985         var valid = true;
6986         items.each(function(f){
6987            if(!f.validate()){
6988                valid = false;
6989                
6990            }
6991         });
6992         return valid;
6993     },
6994     /**
6995      * Returns true if any fields in this form have changed since their original load.
6996      * @return Boolean
6997      */
6998     isDirty : function(){
6999         var dirty = false;
7000         var items = this.getItems();
7001         items.each(function(f){
7002            if(f.isDirty()){
7003                dirty = true;
7004                return false;
7005            }
7006            return true;
7007         });
7008         return dirty;
7009     },
7010      /**
7011      * Performs a predefined action (submit or load) or custom actions you define on this form.
7012      * @param {String} actionName The name of the action type
7013      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7014      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7015      * accept other config options):
7016      * <pre>
7017 Property          Type             Description
7018 ----------------  ---------------  ----------------------------------------------------------------------------------
7019 url               String           The url for the action (defaults to the form's url)
7020 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7021 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7022 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7023                                    validate the form on the client (defaults to false)
7024      * </pre>
7025      * @return {BasicForm} this
7026      */
7027     doAction : function(action, options){
7028         if(typeof action == 'string'){
7029             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7030         }
7031         if(this.fireEvent('beforeaction', this, action) !== false){
7032             this.beforeAction(action);
7033             action.run.defer(100, action);
7034         }
7035         return this;
7036     },
7037     
7038     // private
7039     beforeAction : function(action){
7040         var o = action.options;
7041         
7042         if(this.loadMask){
7043             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7044         }
7045         // not really supported yet.. ??
7046         
7047         //if(this.waitMsgTarget === true){
7048         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7049         //}else if(this.waitMsgTarget){
7050         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7051         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7052         //}else {
7053         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7054        // }
7055          
7056     },
7057
7058     // private
7059     afterAction : function(action, success){
7060         this.activeAction = null;
7061         var o = action.options;
7062         
7063         //if(this.waitMsgTarget === true){
7064             this.el.unmask();
7065         //}else if(this.waitMsgTarget){
7066         //    this.waitMsgTarget.unmask();
7067         //}else{
7068         //    Roo.MessageBox.updateProgress(1);
7069         //    Roo.MessageBox.hide();
7070        // }
7071         // 
7072         if(success){
7073             if(o.reset){
7074                 this.reset();
7075             }
7076             Roo.callback(o.success, o.scope, [this, action]);
7077             this.fireEvent('actioncomplete', this, action);
7078             
7079         }else{
7080             
7081             // failure condition..
7082             // we have a scenario where updates need confirming.
7083             // eg. if a locking scenario exists..
7084             // we look for { errors : { needs_confirm : true }} in the response.
7085             if (
7086                 (typeof(action.result) != 'undefined')  &&
7087                 (typeof(action.result.errors) != 'undefined')  &&
7088                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7089            ){
7090                 var _t = this;
7091                 Roo.log("not supported yet");
7092                  /*
7093                 
7094                 Roo.MessageBox.confirm(
7095                     "Change requires confirmation",
7096                     action.result.errorMsg,
7097                     function(r) {
7098                         if (r != 'yes') {
7099                             return;
7100                         }
7101                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7102                     }
7103                     
7104                 );
7105                 */
7106                 
7107                 
7108                 return;
7109             }
7110             
7111             Roo.callback(o.failure, o.scope, [this, action]);
7112             // show an error message if no failed handler is set..
7113             if (!this.hasListener('actionfailed')) {
7114                 Roo.log("need to add dialog support");
7115                 /*
7116                 Roo.MessageBox.alert("Error",
7117                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7118                         action.result.errorMsg :
7119                         "Saving Failed, please check your entries or try again"
7120                 );
7121                 */
7122             }
7123             
7124             this.fireEvent('actionfailed', this, action);
7125         }
7126         
7127     },
7128     /**
7129      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7130      * @param {String} id The value to search for
7131      * @return Field
7132      */
7133     findField : function(id){
7134         var items = this.getItems();
7135         var field = items.get(id);
7136         if(!field){
7137              items.each(function(f){
7138                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7139                     field = f;
7140                     return false;
7141                 }
7142                 return true;
7143             });
7144         }
7145         return field || null;
7146     },
7147      /**
7148      * Mark fields in this form invalid in bulk.
7149      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7150      * @return {BasicForm} this
7151      */
7152     markInvalid : function(errors){
7153         if(errors instanceof Array){
7154             for(var i = 0, len = errors.length; i < len; i++){
7155                 var fieldError = errors[i];
7156                 var f = this.findField(fieldError.id);
7157                 if(f){
7158                     f.markInvalid(fieldError.msg);
7159                 }
7160             }
7161         }else{
7162             var field, id;
7163             for(id in errors){
7164                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7165                     field.markInvalid(errors[id]);
7166                 }
7167             }
7168         }
7169         //Roo.each(this.childForms || [], function (f) {
7170         //    f.markInvalid(errors);
7171         //});
7172         
7173         return this;
7174     },
7175
7176     /**
7177      * Set values for fields in this form in bulk.
7178      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7179      * @return {BasicForm} this
7180      */
7181     setValues : function(values){
7182         if(values instanceof Array){ // array of objects
7183             for(var i = 0, len = values.length; i < len; i++){
7184                 var v = values[i];
7185                 var f = this.findField(v.id);
7186                 if(f){
7187                     f.setValue(v.value);
7188                     if(this.trackResetOnLoad){
7189                         f.originalValue = f.getValue();
7190                     }
7191                 }
7192             }
7193         }else{ // object hash
7194             var field, id;
7195             for(id in values){
7196                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7197                     
7198                     if (field.setFromData && 
7199                         field.valueField && 
7200                         field.displayField &&
7201                         // combos' with local stores can 
7202                         // be queried via setValue()
7203                         // to set their value..
7204                         (field.store && !field.store.isLocal)
7205                         ) {
7206                         // it's a combo
7207                         var sd = { };
7208                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7209                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7210                         field.setFromData(sd);
7211                         
7212                     } else {
7213                         field.setValue(values[id]);
7214                     }
7215                     
7216                     
7217                     if(this.trackResetOnLoad){
7218                         field.originalValue = field.getValue();
7219                     }
7220                 }
7221             }
7222         }
7223          
7224         //Roo.each(this.childForms || [], function (f) {
7225         //    f.setValues(values);
7226         //});
7227                 
7228         return this;
7229     },
7230
7231     /**
7232      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7233      * they are returned as an array.
7234      * @param {Boolean} asString
7235      * @return {Object}
7236      */
7237     getValues : function(asString){
7238         //if (this.childForms) {
7239             // copy values from the child forms
7240         //    Roo.each(this.childForms, function (f) {
7241         //        this.setValues(f.getValues());
7242         //    }, this);
7243         //}
7244         
7245         
7246         
7247         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7248         if(asString === true){
7249             return fs;
7250         }
7251         return Roo.urlDecode(fs);
7252     },
7253     
7254     /**
7255      * Returns the fields in this form as an object with key/value pairs. 
7256      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7257      * @return {Object}
7258      */
7259     getFieldValues : function(with_hidden)
7260     {
7261         var items = this.getItems();
7262         var ret = {};
7263         items.each(function(f){
7264             if (!f.getName()) {
7265                 return;
7266             }
7267             var v = f.getValue();
7268             if (f.inputType =='radio') {
7269                 if (typeof(ret[f.getName()]) == 'undefined') {
7270                     ret[f.getName()] = ''; // empty..
7271                 }
7272                 
7273                 if (!f.el.dom.checked) {
7274                     return;
7275                     
7276                 }
7277                 v = f.el.dom.value;
7278                 
7279             }
7280             
7281             // not sure if this supported any more..
7282             if ((typeof(v) == 'object') && f.getRawValue) {
7283                 v = f.getRawValue() ; // dates..
7284             }
7285             // combo boxes where name != hiddenName...
7286             if (f.name != f.getName()) {
7287                 ret[f.name] = f.getRawValue();
7288             }
7289             ret[f.getName()] = v;
7290         });
7291         
7292         return ret;
7293     },
7294
7295     /**
7296      * Clears all invalid messages in this form.
7297      * @return {BasicForm} this
7298      */
7299     clearInvalid : function(){
7300         var items = this.getItems();
7301         
7302         items.each(function(f){
7303            f.clearInvalid();
7304         });
7305         
7306         
7307         
7308         return this;
7309     },
7310
7311     /**
7312      * Resets this form.
7313      * @return {BasicForm} this
7314      */
7315     reset : function(){
7316         var items = this.getItems();
7317         items.each(function(f){
7318             f.reset();
7319         });
7320         
7321         Roo.each(this.childForms || [], function (f) {
7322             f.reset();
7323         });
7324        
7325         
7326         return this;
7327     },
7328     getItems : function()
7329     {
7330         var r=new Roo.util.MixedCollection(false, function(o){
7331             return o.id || (o.id = Roo.id());
7332         });
7333         var iter = function(el) {
7334             if (el.inputEl) {
7335                 r.add(el);
7336             }
7337             if (!el.items) {
7338                 return;
7339             }
7340             Roo.each(el.items,function(e) {
7341                 iter(e);
7342             });
7343             
7344             
7345         };
7346         
7347         iter(this);
7348         return r;
7349         
7350         
7351         
7352         
7353     }
7354     
7355 });
7356
7357  
7358 /*
7359  * Based on:
7360  * Ext JS Library 1.1.1
7361  * Copyright(c) 2006-2007, Ext JS, LLC.
7362  *
7363  * Originally Released Under LGPL - original licence link has changed is not relivant.
7364  *
7365  * Fork - LGPL
7366  * <script type="text/javascript">
7367  */
7368 /**
7369  * @class Roo.form.VTypes
7370  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7371  * @singleton
7372  */
7373 Roo.form.VTypes = function(){
7374     // closure these in so they are only created once.
7375     var alpha = /^[a-zA-Z_]+$/;
7376     var alphanum = /^[a-zA-Z0-9_]+$/;
7377     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7378     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7379
7380     // All these messages and functions are configurable
7381     return {
7382         /**
7383          * The function used to validate email addresses
7384          * @param {String} value The email address
7385          */
7386         'email' : function(v){
7387             return email.test(v);
7388         },
7389         /**
7390          * The error text to display when the email validation function returns false
7391          * @type String
7392          */
7393         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7394         /**
7395          * The keystroke filter mask to be applied on email input
7396          * @type RegExp
7397          */
7398         'emailMask' : /[a-z0-9_\.\-@]/i,
7399
7400         /**
7401          * The function used to validate URLs
7402          * @param {String} value The URL
7403          */
7404         'url' : function(v){
7405             return url.test(v);
7406         },
7407         /**
7408          * The error text to display when the url validation function returns false
7409          * @type String
7410          */
7411         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7412         
7413         /**
7414          * The function used to validate alpha values
7415          * @param {String} value The value
7416          */
7417         'alpha' : function(v){
7418             return alpha.test(v);
7419         },
7420         /**
7421          * The error text to display when the alpha validation function returns false
7422          * @type String
7423          */
7424         'alphaText' : 'This field should only contain letters and _',
7425         /**
7426          * The keystroke filter mask to be applied on alpha input
7427          * @type RegExp
7428          */
7429         'alphaMask' : /[a-z_]/i,
7430
7431         /**
7432          * The function used to validate alphanumeric values
7433          * @param {String} value The value
7434          */
7435         'alphanum' : function(v){
7436             return alphanum.test(v);
7437         },
7438         /**
7439          * The error text to display when the alphanumeric validation function returns false
7440          * @type String
7441          */
7442         'alphanumText' : 'This field should only contain letters, numbers and _',
7443         /**
7444          * The keystroke filter mask to be applied on alphanumeric input
7445          * @type RegExp
7446          */
7447         'alphanumMask' : /[a-z0-9_]/i
7448     };
7449 }();/*
7450  * - LGPL
7451  *
7452  * Input
7453  * 
7454  */
7455
7456 /**
7457  * @class Roo.bootstrap.Input
7458  * @extends Roo.bootstrap.Component
7459  * Bootstrap Input class
7460  * @cfg {Boolean} disabled is it disabled
7461  * @cfg {String} fieldLabel - the label associated
7462  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7463  * @cfg {String} name name of the input
7464  * @cfg {string} fieldLabel - the label associated
7465  * @cfg {string}  inputType - input / file submit ...
7466  * @cfg {string} placeholder - placeholder to put in text.
7467  * @cfg {string}  before - input group add on before
7468  * @cfg {string} after - input group add on after
7469  * @cfg {string} size - (lg|sm) or leave empty..
7470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7472  * @cfg {Number} md colspan out of 12 for computer-sized screens
7473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7474  * @cfg {string} value default value of the input
7475  * @cfg {Number} labelWidth set the width of label (0-12)
7476  * @cfg {String} labelAlign (top|left)
7477  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7478  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7479
7480  * @cfg {String} align (left|center|right) Default left
7481  * @cfg {Boolean} forceFeedback (true|false) Default false
7482  * 
7483  * 
7484  * 
7485  * @constructor
7486  * Create a new Input
7487  * @param {Object} config The config object
7488  */
7489
7490 Roo.bootstrap.Input = function(config){
7491     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7492    
7493         this.addEvents({
7494             /**
7495              * @event focus
7496              * Fires when this field receives input focus.
7497              * @param {Roo.form.Field} this
7498              */
7499             focus : true,
7500             /**
7501              * @event blur
7502              * Fires when this field loses input focus.
7503              * @param {Roo.form.Field} this
7504              */
7505             blur : true,
7506             /**
7507              * @event specialkey
7508              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7509              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7510              * @param {Roo.form.Field} this
7511              * @param {Roo.EventObject} e The event object
7512              */
7513             specialkey : true,
7514             /**
7515              * @event change
7516              * Fires just before the field blurs if the field value has changed.
7517              * @param {Roo.form.Field} this
7518              * @param {Mixed} newValue The new value
7519              * @param {Mixed} oldValue The original value
7520              */
7521             change : true,
7522             /**
7523              * @event invalid
7524              * Fires after the field has been marked as invalid.
7525              * @param {Roo.form.Field} this
7526              * @param {String} msg The validation message
7527              */
7528             invalid : true,
7529             /**
7530              * @event valid
7531              * Fires after the field has been validated with no errors.
7532              * @param {Roo.form.Field} this
7533              */
7534             valid : true,
7535              /**
7536              * @event keyup
7537              * Fires after the key up
7538              * @param {Roo.form.Field} this
7539              * @param {Roo.EventObject}  e The event Object
7540              */
7541             keyup : true
7542         });
7543 };
7544
7545 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7546      /**
7547      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7548       automatic validation (defaults to "keyup").
7549      */
7550     validationEvent : "keyup",
7551      /**
7552      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7553      */
7554     validateOnBlur : true,
7555     /**
7556      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7557      */
7558     validationDelay : 250,
7559      /**
7560      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7561      */
7562     focusClass : "x-form-focus",  // not needed???
7563     
7564        
7565     /**
7566      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7567      */
7568     invalidClass : "has-warning",
7569     
7570     /**
7571      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7572      */
7573     validClass : "has-success",
7574     
7575     /**
7576      * @cfg {Boolean} hasFeedback (true|false) default true
7577      */
7578     hasFeedback : true,
7579     
7580     /**
7581      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7582      */
7583     invalidFeedbackClass : "glyphicon-warning-sign",
7584     
7585     /**
7586      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7587      */
7588     validFeedbackClass : "glyphicon-ok",
7589     
7590     /**
7591      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7592      */
7593     selectOnFocus : false,
7594     
7595      /**
7596      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7597      */
7598     maskRe : null,
7599        /**
7600      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7601      */
7602     vtype : null,
7603     
7604       /**
7605      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7606      */
7607     disableKeyFilter : false,
7608     
7609        /**
7610      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7611      */
7612     disabled : false,
7613      /**
7614      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7615      */
7616     allowBlank : true,
7617     /**
7618      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7619      */
7620     blankText : "This field is required",
7621     
7622      /**
7623      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7624      */
7625     minLength : 0,
7626     /**
7627      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7628      */
7629     maxLength : Number.MAX_VALUE,
7630     /**
7631      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7632      */
7633     minLengthText : "The minimum length for this field is {0}",
7634     /**
7635      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7636      */
7637     maxLengthText : "The maximum length for this field is {0}",
7638   
7639     
7640     /**
7641      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7642      * If available, this function will be called only after the basic validators all return true, and will be passed the
7643      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7644      */
7645     validator : null,
7646     /**
7647      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7648      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7649      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7650      */
7651     regex : null,
7652     /**
7653      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7654      */
7655     regexText : "",
7656     
7657     autocomplete: false,
7658     
7659     
7660     fieldLabel : '',
7661     inputType : 'text',
7662     
7663     name : false,
7664     placeholder: false,
7665     before : false,
7666     after : false,
7667     size : false,
7668     hasFocus : false,
7669     preventMark: false,
7670     isFormField : true,
7671     value : '',
7672     labelWidth : 2,
7673     labelAlign : false,
7674     readOnly : false,
7675     align : false,
7676     formatedValue : false,
7677     forceFeedback : false,
7678     
7679     parentLabelAlign : function()
7680     {
7681         var parent = this;
7682         while (parent.parent()) {
7683             parent = parent.parent();
7684             if (typeof(parent.labelAlign) !='undefined') {
7685                 return parent.labelAlign;
7686             }
7687         }
7688         return 'left';
7689         
7690     },
7691     
7692     getAutoCreate : function(){
7693         
7694         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7695         
7696         var id = Roo.id();
7697         
7698         var cfg = {};
7699         
7700         if(this.inputType != 'hidden'){
7701             cfg.cls = 'form-group' //input-group
7702         }
7703         
7704         var input =  {
7705             tag: 'input',
7706             id : id,
7707             type : this.inputType,
7708             value : this.value,
7709             cls : 'form-control',
7710             placeholder : this.placeholder || '',
7711             autocomplete : this.autocomplete || 'new-password'
7712         };
7713         
7714         
7715         if(this.align){
7716             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7717         }
7718         
7719         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7720             input.maxLength = this.maxLength;
7721         }
7722         
7723         if (this.disabled) {
7724             input.disabled=true;
7725         }
7726         
7727         if (this.readOnly) {
7728             input.readonly=true;
7729         }
7730         
7731         if (this.name) {
7732             input.name = this.name;
7733         }
7734         if (this.size) {
7735             input.cls += ' input-' + this.size;
7736         }
7737         var settings=this;
7738         ['xs','sm','md','lg'].map(function(size){
7739             if (settings[size]) {
7740                 cfg.cls += ' col-' + size + '-' + settings[size];
7741             }
7742         });
7743         
7744         var inputblock = input;
7745         
7746         var feedback = {
7747             tag: 'span',
7748             cls: 'glyphicon form-control-feedback'
7749         };
7750             
7751         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7752             
7753             inputblock = {
7754                 cls : 'has-feedback',
7755                 cn :  [
7756                     input,
7757                     feedback
7758                 ] 
7759             };  
7760         }
7761         
7762         if (this.before || this.after) {
7763             
7764             inputblock = {
7765                 cls : 'input-group',
7766                 cn :  [] 
7767             };
7768             
7769             if (this.before && typeof(this.before) == 'string') {
7770                 
7771                 inputblock.cn.push({
7772                     tag :'span',
7773                     cls : 'roo-input-before input-group-addon',
7774                     html : this.before
7775                 });
7776             }
7777             if (this.before && typeof(this.before) == 'object') {
7778                 this.before = Roo.factory(this.before);
7779                 Roo.log(this.before);
7780                 inputblock.cn.push({
7781                     tag :'span',
7782                     cls : 'roo-input-before input-group-' +
7783                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7784                 });
7785             }
7786             
7787             inputblock.cn.push(input);
7788             
7789             if (this.after && typeof(this.after) == 'string') {
7790                 inputblock.cn.push({
7791                     tag :'span',
7792                     cls : 'roo-input-after input-group-addon',
7793                     html : this.after
7794                 });
7795             }
7796             if (this.after && typeof(this.after) == 'object') {
7797                 this.after = Roo.factory(this.after);
7798                 Roo.log(this.after);
7799                 inputblock.cn.push({
7800                     tag :'span',
7801                     cls : 'roo-input-after input-group-' +
7802                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7803                 });
7804             }
7805             
7806             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7807                 inputblock.cls += ' has-feedback';
7808                 inputblock.cn.push(feedback);
7809             }
7810         };
7811         
7812         if (align ==='left' && this.fieldLabel.length) {
7813                 Roo.log("left and has label");
7814                 cfg.cn = [
7815                     
7816                     {
7817                         tag: 'label',
7818                         'for' :  id,
7819                         cls : 'control-label col-sm-' + this.labelWidth,
7820                         html : this.fieldLabel
7821                         
7822                     },
7823                     {
7824                         cls : "col-sm-" + (12 - this.labelWidth), 
7825                         cn: [
7826                             inputblock
7827                         ]
7828                     }
7829                     
7830                 ];
7831         } else if ( this.fieldLabel.length) {
7832                 Roo.log(" label");
7833                  cfg.cn = [
7834                    
7835                     {
7836                         tag: 'label',
7837                         //cls : 'input-group-addon',
7838                         html : this.fieldLabel
7839                         
7840                     },
7841                     
7842                     inputblock
7843                     
7844                 ];
7845
7846         } else {
7847             
7848                 Roo.log(" no label && no align");
7849                 cfg.cn = [
7850                     
7851                         inputblock
7852                     
7853                 ];
7854                 
7855                 
7856         };
7857         Roo.log('input-parentType: ' + this.parentType);
7858         
7859         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7860            cfg.cls += ' navbar-form';
7861            Roo.log(cfg);
7862         }
7863         
7864         return cfg;
7865         
7866     },
7867     /**
7868      * return the real input element.
7869      */
7870     inputEl: function ()
7871     {
7872         return this.el.select('input.form-control',true).first();
7873     },
7874     
7875     tooltipEl : function()
7876     {
7877         return this.inputEl();
7878     },
7879     
7880     setDisabled : function(v)
7881     {
7882         var i  = this.inputEl().dom;
7883         if (!v) {
7884             i.removeAttribute('disabled');
7885             return;
7886             
7887         }
7888         i.setAttribute('disabled','true');
7889     },
7890     initEvents : function()
7891     {
7892           
7893         this.inputEl().on("keydown" , this.fireKey,  this);
7894         this.inputEl().on("focus", this.onFocus,  this);
7895         this.inputEl().on("blur", this.onBlur,  this);
7896         
7897         this.inputEl().relayEvent('keyup', this);
7898  
7899         // reference to original value for reset
7900         this.originalValue = this.getValue();
7901         //Roo.form.TextField.superclass.initEvents.call(this);
7902         if(this.validationEvent == 'keyup'){
7903             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7904             this.inputEl().on('keyup', this.filterValidation, this);
7905         }
7906         else if(this.validationEvent !== false){
7907             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7908         }
7909         
7910         if(this.selectOnFocus){
7911             this.on("focus", this.preFocus, this);
7912             
7913         }
7914         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7915             this.inputEl().on("keypress", this.filterKeys, this);
7916         }
7917        /* if(this.grow){
7918             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7919             this.el.on("click", this.autoSize,  this);
7920         }
7921         */
7922         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7923             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7924         }
7925         
7926         if (typeof(this.before) == 'object') {
7927             this.before.render(this.el.select('.roo-input-before',true).first());
7928         }
7929         if (typeof(this.after) == 'object') {
7930             this.after.render(this.el.select('.roo-input-after',true).first());
7931         }
7932         
7933         
7934     },
7935     filterValidation : function(e){
7936         if(!e.isNavKeyPress()){
7937             this.validationTask.delay(this.validationDelay);
7938         }
7939     },
7940      /**
7941      * Validates the field value
7942      * @return {Boolean} True if the value is valid, else false
7943      */
7944     validate : function(){
7945         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7946         if(this.disabled || this.validateValue(this.getRawValue())){
7947             this.markValid();
7948             return true;
7949         }
7950         
7951         this.markInvalid();
7952         return false;
7953     },
7954     
7955     
7956     /**
7957      * Validates a value according to the field's validation rules and marks the field as invalid
7958      * if the validation fails
7959      * @param {Mixed} value The value to validate
7960      * @return {Boolean} True if the value is valid, else false
7961      */
7962     validateValue : function(value){
7963         if(value.length < 1)  { // if it's blank
7964             if(this.allowBlank){
7965                 return true;
7966             }
7967             return false;
7968         }
7969         
7970         if(value.length < this.minLength){
7971             return false;
7972         }
7973         if(value.length > this.maxLength){
7974             return false;
7975         }
7976         if(this.vtype){
7977             var vt = Roo.form.VTypes;
7978             if(!vt[this.vtype](value, this)){
7979                 return false;
7980             }
7981         }
7982         if(typeof this.validator == "function"){
7983             var msg = this.validator(value);
7984             if(msg !== true){
7985                 return false;
7986             }
7987         }
7988         
7989         if(this.regex && !this.regex.test(value)){
7990             return false;
7991         }
7992         
7993         return true;
7994     },
7995
7996     
7997     
7998      // private
7999     fireKey : function(e){
8000         //Roo.log('field ' + e.getKey());
8001         if(e.isNavKeyPress()){
8002             this.fireEvent("specialkey", this, e);
8003         }
8004     },
8005     focus : function (selectText){
8006         if(this.rendered){
8007             this.inputEl().focus();
8008             if(selectText === true){
8009                 this.inputEl().dom.select();
8010             }
8011         }
8012         return this;
8013     } ,
8014     
8015     onFocus : function(){
8016         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8017            // this.el.addClass(this.focusClass);
8018         }
8019         if(!this.hasFocus){
8020             this.hasFocus = true;
8021             this.startValue = this.getValue();
8022             this.fireEvent("focus", this);
8023         }
8024     },
8025     
8026     beforeBlur : Roo.emptyFn,
8027
8028     
8029     // private
8030     onBlur : function(){
8031         this.beforeBlur();
8032         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8033             //this.el.removeClass(this.focusClass);
8034         }
8035         this.hasFocus = false;
8036         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8037             this.validate();
8038         }
8039         var v = this.getValue();
8040         if(String(v) !== String(this.startValue)){
8041             this.fireEvent('change', this, v, this.startValue);
8042         }
8043         this.fireEvent("blur", this);
8044     },
8045     
8046     /**
8047      * Resets the current field value to the originally loaded value and clears any validation messages
8048      */
8049     reset : function(){
8050         this.setValue(this.originalValue);
8051         this.validate();
8052     },
8053      /**
8054      * Returns the name of the field
8055      * @return {Mixed} name The name field
8056      */
8057     getName: function(){
8058         return this.name;
8059     },
8060      /**
8061      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8062      * @return {Mixed} value The field value
8063      */
8064     getValue : function(){
8065         
8066         var v = this.inputEl().getValue();
8067         
8068         return v;
8069     },
8070     /**
8071      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8072      * @return {Mixed} value The field value
8073      */
8074     getRawValue : function(){
8075         var v = this.inputEl().getValue();
8076         
8077         return v;
8078     },
8079     
8080     /**
8081      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8082      * @param {Mixed} value The value to set
8083      */
8084     setRawValue : function(v){
8085         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8086     },
8087     
8088     selectText : function(start, end){
8089         var v = this.getRawValue();
8090         if(v.length > 0){
8091             start = start === undefined ? 0 : start;
8092             end = end === undefined ? v.length : end;
8093             var d = this.inputEl().dom;
8094             if(d.setSelectionRange){
8095                 d.setSelectionRange(start, end);
8096             }else if(d.createTextRange){
8097                 var range = d.createTextRange();
8098                 range.moveStart("character", start);
8099                 range.moveEnd("character", v.length-end);
8100                 range.select();
8101             }
8102         }
8103     },
8104     
8105     /**
8106      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8107      * @param {Mixed} value The value to set
8108      */
8109     setValue : function(v){
8110         this.value = v;
8111         if(this.rendered){
8112             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8113             this.validate();
8114         }
8115     },
8116     
8117     /*
8118     processValue : function(value){
8119         if(this.stripCharsRe){
8120             var newValue = value.replace(this.stripCharsRe, '');
8121             if(newValue !== value){
8122                 this.setRawValue(newValue);
8123                 return newValue;
8124             }
8125         }
8126         return value;
8127     },
8128   */
8129     preFocus : function(){
8130         
8131         if(this.selectOnFocus){
8132             this.inputEl().dom.select();
8133         }
8134     },
8135     filterKeys : function(e){
8136         var k = e.getKey();
8137         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8138             return;
8139         }
8140         var c = e.getCharCode(), cc = String.fromCharCode(c);
8141         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8142             return;
8143         }
8144         if(!this.maskRe.test(cc)){
8145             e.stopEvent();
8146         }
8147     },
8148      /**
8149      * Clear any invalid styles/messages for this field
8150      */
8151     clearInvalid : function(){
8152         
8153         if(!this.el || this.preventMark){ // not rendered
8154             return;
8155         }
8156         this.el.removeClass(this.invalidClass);
8157         
8158         this.fireEvent('valid', this);
8159     },
8160     
8161      /**
8162      * Mark this field as valid
8163      */
8164     markValid : function(){
8165         if(!this.el  || this.preventMark){ // not rendered
8166             return;
8167         }
8168         
8169         this.el.removeClass([this.invalidClass, this.validClass]);
8170         
8171         if(this.disabled || this.allowBlank){
8172             return;
8173         }
8174         
8175         this.el.addClass(this.validClass);
8176         
8177         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8178             
8179             var feedback = this.el.select('.form-control-feedback', true).first();
8180             
8181             if(feedback){
8182                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8183                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8184             }
8185             
8186         }
8187         
8188         this.fireEvent('valid', this);
8189     },
8190     
8191      /**
8192      * Mark this field as invalid
8193      * @param {String} msg The validation message
8194      */
8195     markInvalid : function(msg){
8196         if(!this.el  || this.preventMark){ // not rendered
8197             return;
8198         }
8199         
8200         this.el.removeClass([this.invalidClass, this.validClass]);
8201         
8202         if(this.disabled || this.allowBlank){
8203             return;
8204         }
8205         
8206         this.el.addClass(this.invalidClass);
8207         
8208         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8209             
8210             var feedback = this.el.select('.form-control-feedback', true).first();
8211             
8212             if(feedback){
8213                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8214                 
8215                 if(this.getValue().length || this.forceFeedback){
8216                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8217                 }
8218                 
8219             }
8220             
8221         }
8222         
8223         this.fireEvent('invalid', this, msg);
8224     },
8225     // private
8226     SafariOnKeyDown : function(event)
8227     {
8228         // this is a workaround for a password hang bug on chrome/ webkit.
8229         
8230         var isSelectAll = false;
8231         
8232         if(this.inputEl().dom.selectionEnd > 0){
8233             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8234         }
8235         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8236             event.preventDefault();
8237             this.setValue('');
8238             return;
8239         }
8240         
8241         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8242             
8243             event.preventDefault();
8244             // this is very hacky as keydown always get's upper case.
8245             //
8246             var cc = String.fromCharCode(event.getCharCode());
8247             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8248             
8249         }
8250     },
8251     adjustWidth : function(tag, w){
8252         tag = tag.toLowerCase();
8253         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8254             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8255                 if(tag == 'input'){
8256                     return w + 2;
8257                 }
8258                 if(tag == 'textarea'){
8259                     return w-2;
8260                 }
8261             }else if(Roo.isOpera){
8262                 if(tag == 'input'){
8263                     return w + 2;
8264                 }
8265                 if(tag == 'textarea'){
8266                     return w-2;
8267                 }
8268             }
8269         }
8270         return w;
8271     }
8272     
8273 });
8274
8275  
8276 /*
8277  * - LGPL
8278  *
8279  * Input
8280  * 
8281  */
8282
8283 /**
8284  * @class Roo.bootstrap.TextArea
8285  * @extends Roo.bootstrap.Input
8286  * Bootstrap TextArea class
8287  * @cfg {Number} cols Specifies the visible width of a text area
8288  * @cfg {Number} rows Specifies the visible number of lines in a text area
8289  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8290  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8291  * @cfg {string} html text
8292  * 
8293  * @constructor
8294  * Create a new TextArea
8295  * @param {Object} config The config object
8296  */
8297
8298 Roo.bootstrap.TextArea = function(config){
8299     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8300    
8301 };
8302
8303 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8304      
8305     cols : false,
8306     rows : 5,
8307     readOnly : false,
8308     warp : 'soft',
8309     resize : false,
8310     value: false,
8311     html: false,
8312     
8313     getAutoCreate : function(){
8314         
8315         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8316         
8317         var id = Roo.id();
8318         
8319         var cfg = {};
8320         
8321         var input =  {
8322             tag: 'textarea',
8323             id : id,
8324             warp : this.warp,
8325             rows : this.rows,
8326             value : this.value || '',
8327             html: this.html || '',
8328             cls : 'form-control',
8329             placeholder : this.placeholder || '' 
8330             
8331         };
8332         
8333         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8334             input.maxLength = this.maxLength;
8335         }
8336         
8337         if(this.resize){
8338             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8339         }
8340         
8341         if(this.cols){
8342             input.cols = this.cols;
8343         }
8344         
8345         if (this.readOnly) {
8346             input.readonly = true;
8347         }
8348         
8349         if (this.name) {
8350             input.name = this.name;
8351         }
8352         
8353         if (this.size) {
8354             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8355         }
8356         
8357         var settings=this;
8358         ['xs','sm','md','lg'].map(function(size){
8359             if (settings[size]) {
8360                 cfg.cls += ' col-' + size + '-' + settings[size];
8361             }
8362         });
8363         
8364         var inputblock = input;
8365         
8366         if(this.hasFeedback && !this.allowBlank){
8367             
8368             var feedback = {
8369                 tag: 'span',
8370                 cls: 'glyphicon form-control-feedback'
8371             };
8372
8373             inputblock = {
8374                 cls : 'has-feedback',
8375                 cn :  [
8376                     input,
8377                     feedback
8378                 ] 
8379             };  
8380         }
8381         
8382         
8383         if (this.before || this.after) {
8384             
8385             inputblock = {
8386                 cls : 'input-group',
8387                 cn :  [] 
8388             };
8389             if (this.before) {
8390                 inputblock.cn.push({
8391                     tag :'span',
8392                     cls : 'input-group-addon',
8393                     html : this.before
8394                 });
8395             }
8396             
8397             inputblock.cn.push(input);
8398             
8399             if(this.hasFeedback && !this.allowBlank){
8400                 inputblock.cls += ' has-feedback';
8401                 inputblock.cn.push(feedback);
8402             }
8403             
8404             if (this.after) {
8405                 inputblock.cn.push({
8406                     tag :'span',
8407                     cls : 'input-group-addon',
8408                     html : this.after
8409                 });
8410             }
8411             
8412         }
8413         
8414         if (align ==='left' && this.fieldLabel.length) {
8415                 Roo.log("left and has label");
8416                 cfg.cn = [
8417                     
8418                     {
8419                         tag: 'label',
8420                         'for' :  id,
8421                         cls : 'control-label col-sm-' + this.labelWidth,
8422                         html : this.fieldLabel
8423                         
8424                     },
8425                     {
8426                         cls : "col-sm-" + (12 - this.labelWidth), 
8427                         cn: [
8428                             inputblock
8429                         ]
8430                     }
8431                     
8432                 ];
8433         } else if ( this.fieldLabel.length) {
8434                 Roo.log(" label");
8435                  cfg.cn = [
8436                    
8437                     {
8438                         tag: 'label',
8439                         //cls : 'input-group-addon',
8440                         html : this.fieldLabel
8441                         
8442                     },
8443                     
8444                     inputblock
8445                     
8446                 ];
8447
8448         } else {
8449             
8450                    Roo.log(" no label && no align");
8451                 cfg.cn = [
8452                     
8453                         inputblock
8454                     
8455                 ];
8456                 
8457                 
8458         }
8459         
8460         if (this.disabled) {
8461             input.disabled=true;
8462         }
8463         
8464         return cfg;
8465         
8466     },
8467     /**
8468      * return the real textarea element.
8469      */
8470     inputEl: function ()
8471     {
8472         return this.el.select('textarea.form-control',true).first();
8473     }
8474 });
8475
8476  
8477 /*
8478  * - LGPL
8479  *
8480  * trigger field - base class for combo..
8481  * 
8482  */
8483  
8484 /**
8485  * @class Roo.bootstrap.TriggerField
8486  * @extends Roo.bootstrap.Input
8487  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8488  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8489  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8490  * for which you can provide a custom implementation.  For example:
8491  * <pre><code>
8492 var trigger = new Roo.bootstrap.TriggerField();
8493 trigger.onTriggerClick = myTriggerFn;
8494 trigger.applyTo('my-field');
8495 </code></pre>
8496  *
8497  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8498  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8499  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8500  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8501  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8502
8503  * @constructor
8504  * Create a new TriggerField.
8505  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8506  * to the base TextField)
8507  */
8508 Roo.bootstrap.TriggerField = function(config){
8509     this.mimicing = false;
8510     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8511 };
8512
8513 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8514     /**
8515      * @cfg {String} triggerClass A CSS class to apply to the trigger
8516      */
8517      /**
8518      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8519      */
8520     hideTrigger:false,
8521
8522     /**
8523      * @cfg {Boolean} removable (true|false) special filter default false
8524      */
8525     removable : false,
8526     
8527     /** @cfg {Boolean} grow @hide */
8528     /** @cfg {Number} growMin @hide */
8529     /** @cfg {Number} growMax @hide */
8530
8531     /**
8532      * @hide 
8533      * @method
8534      */
8535     autoSize: Roo.emptyFn,
8536     // private
8537     monitorTab : true,
8538     // private
8539     deferHeight : true,
8540
8541     
8542     actionMode : 'wrap',
8543     
8544     caret : false,
8545     
8546     
8547     getAutoCreate : function(){
8548        
8549         var align = this.labelAlign || this.parentLabelAlign();
8550         
8551         var id = Roo.id();
8552         
8553         var cfg = {
8554             cls: 'form-group' //input-group
8555         };
8556         
8557         
8558         var input =  {
8559             tag: 'input',
8560             id : id,
8561             type : this.inputType,
8562             cls : 'form-control',
8563             autocomplete: 'new-password',
8564             placeholder : this.placeholder || '' 
8565             
8566         };
8567         if (this.name) {
8568             input.name = this.name;
8569         }
8570         if (this.size) {
8571             input.cls += ' input-' + this.size;
8572         }
8573         
8574         if (this.disabled) {
8575             input.disabled=true;
8576         }
8577         
8578         var inputblock = input;
8579         
8580         if(this.hasFeedback && !this.allowBlank){
8581             
8582             var feedback = {
8583                 tag: 'span',
8584                 cls: 'glyphicon form-control-feedback'
8585             };
8586             
8587             if(this.removable && !this.editable && !this.tickable){
8588                 inputblock = {
8589                     cls : 'has-feedback',
8590                     cn :  [
8591                         inputblock,
8592                         {
8593                             tag: 'button',
8594                             html : 'x',
8595                             cls : 'roo-combo-removable-btn close'
8596                         },
8597                         feedback
8598                     ] 
8599                 };
8600             } else {
8601                 inputblock = {
8602                     cls : 'has-feedback',
8603                     cn :  [
8604                         inputblock,
8605                         feedback
8606                     ] 
8607                 };
8608             }
8609
8610         } else {
8611             if(this.removable && !this.editable && !this.tickable){
8612                 inputblock = {
8613                     cls : 'roo-removable',
8614                     cn :  [
8615                         inputblock,
8616                         {
8617                             tag: 'button',
8618                             html : 'x',
8619                             cls : 'roo-combo-removable-btn close'
8620                         }
8621                     ] 
8622                 };
8623             }
8624         }
8625         
8626         if (this.before || this.after) {
8627             
8628             inputblock = {
8629                 cls : 'input-group',
8630                 cn :  [] 
8631             };
8632             if (this.before) {
8633                 inputblock.cn.push({
8634                     tag :'span',
8635                     cls : 'input-group-addon',
8636                     html : this.before
8637                 });
8638             }
8639             
8640             inputblock.cn.push(input);
8641             
8642             if(this.hasFeedback && !this.allowBlank){
8643                 inputblock.cls += ' has-feedback';
8644                 inputblock.cn.push(feedback);
8645             }
8646             
8647             if (this.after) {
8648                 inputblock.cn.push({
8649                     tag :'span',
8650                     cls : 'input-group-addon',
8651                     html : this.after
8652                 });
8653             }
8654             
8655         };
8656         
8657         var box = {
8658             tag: 'div',
8659             cn: [
8660                 {
8661                     tag: 'input',
8662                     type : 'hidden',
8663                     cls: 'form-hidden-field'
8664                 },
8665                 inputblock
8666             ]
8667             
8668         };
8669         
8670         if(this.multiple){
8671             Roo.log('multiple');
8672             
8673             box = {
8674                 tag: 'div',
8675                 cn: [
8676                     {
8677                         tag: 'input',
8678                         type : 'hidden',
8679                         cls: 'form-hidden-field'
8680                     },
8681                     {
8682                         tag: 'ul',
8683                         cls: 'select2-choices',
8684                         cn:[
8685                             {
8686                                 tag: 'li',
8687                                 cls: 'select2-search-field',
8688                                 cn: [
8689
8690                                     inputblock
8691                                 ]
8692                             }
8693                         ]
8694                     }
8695                 ]
8696             }
8697         };
8698         
8699         var combobox = {
8700             cls: 'select2-container input-group',
8701             cn: [
8702                 box
8703 //                {
8704 //                    tag: 'ul',
8705 //                    cls: 'typeahead typeahead-long dropdown-menu',
8706 //                    style: 'display:none'
8707 //                }
8708             ]
8709         };
8710         
8711         if(!this.multiple && this.showToggleBtn){
8712             
8713             var caret = {
8714                         tag: 'span',
8715                         cls: 'caret'
8716              };
8717             if (this.caret != false) {
8718                 caret = {
8719                      tag: 'i',
8720                      cls: 'fa fa-' + this.caret
8721                 };
8722                 
8723             }
8724             
8725             combobox.cn.push({
8726                 tag :'span',
8727                 cls : 'input-group-addon btn dropdown-toggle',
8728                 cn : [
8729                     caret,
8730                     {
8731                         tag: 'span',
8732                         cls: 'combobox-clear',
8733                         cn  : [
8734                             {
8735                                 tag : 'i',
8736                                 cls: 'icon-remove'
8737                             }
8738                         ]
8739                     }
8740                 ]
8741
8742             })
8743         }
8744         
8745         if(this.multiple){
8746             combobox.cls += ' select2-container-multi';
8747         }
8748         
8749         if (align ==='left' && this.fieldLabel.length) {
8750             
8751                 Roo.log("left and has label");
8752                 cfg.cn = [
8753                     
8754                     {
8755                         tag: 'label',
8756                         'for' :  id,
8757                         cls : 'control-label col-sm-' + this.labelWidth,
8758                         html : this.fieldLabel
8759                         
8760                     },
8761                     {
8762                         cls : "col-sm-" + (12 - this.labelWidth), 
8763                         cn: [
8764                             combobox
8765                         ]
8766                     }
8767                     
8768                 ];
8769         } else if ( this.fieldLabel.length) {
8770                 Roo.log(" label");
8771                  cfg.cn = [
8772                    
8773                     {
8774                         tag: 'label',
8775                         //cls : 'input-group-addon',
8776                         html : this.fieldLabel
8777                         
8778                     },
8779                     
8780                     combobox
8781                     
8782                 ];
8783
8784         } else {
8785             
8786                 Roo.log(" no label && no align");
8787                 cfg = combobox
8788                      
8789                 
8790         }
8791          
8792         var settings=this;
8793         ['xs','sm','md','lg'].map(function(size){
8794             if (settings[size]) {
8795                 cfg.cls += ' col-' + size + '-' + settings[size];
8796             }
8797         });
8798         Roo.log(cfg);
8799         return cfg;
8800         
8801     },
8802     
8803     
8804     
8805     // private
8806     onResize : function(w, h){
8807 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8808 //        if(typeof w == 'number'){
8809 //            var x = w - this.trigger.getWidth();
8810 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8811 //            this.trigger.setStyle('left', x+'px');
8812 //        }
8813     },
8814
8815     // private
8816     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8817
8818     // private
8819     getResizeEl : function(){
8820         return this.inputEl();
8821     },
8822
8823     // private
8824     getPositionEl : function(){
8825         return this.inputEl();
8826     },
8827
8828     // private
8829     alignErrorIcon : function(){
8830         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8831     },
8832
8833     // private
8834     initEvents : function(){
8835         
8836         this.createList();
8837         
8838         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8839         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8840         if(!this.multiple && this.showToggleBtn){
8841             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8842             if(this.hideTrigger){
8843                 this.trigger.setDisplayed(false);
8844             }
8845             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8846         }
8847         
8848         if(this.multiple){
8849             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8850         }
8851         
8852         if(this.removable && !this.editable && !this.tickable){
8853             var close = this.closeTriggerEl();
8854             
8855             if(close){
8856                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8857                 close.on('click', this.removeBtnClick, this, close);
8858             }
8859         }
8860         
8861         //this.trigger.addClassOnOver('x-form-trigger-over');
8862         //this.trigger.addClassOnClick('x-form-trigger-click');
8863         
8864         //if(!this.width){
8865         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8866         //}
8867     },
8868     
8869     closeTriggerEl : function()
8870     {
8871         var close = this.el.select('.roo-combo-removable-btn', true).first();
8872         return close ? close : false;
8873     },
8874     
8875     removeBtnClick : function(e, h, el)
8876     {
8877         e.preventDefault();
8878         
8879         if(this.fireEvent("remove", this) !== false){
8880             this.reset();
8881         }
8882     },
8883     
8884     createList : function()
8885     {
8886         this.list = Roo.get(document.body).createChild({
8887             tag: 'ul',
8888             cls: 'typeahead typeahead-long dropdown-menu',
8889             style: 'display:none'
8890         });
8891         
8892         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8893         
8894     },
8895
8896     // private
8897     initTrigger : function(){
8898        
8899     },
8900
8901     // private
8902     onDestroy : function(){
8903         if(this.trigger){
8904             this.trigger.removeAllListeners();
8905           //  this.trigger.remove();
8906         }
8907         //if(this.wrap){
8908         //    this.wrap.remove();
8909         //}
8910         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8911     },
8912
8913     // private
8914     onFocus : function(){
8915         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8916         /*
8917         if(!this.mimicing){
8918             this.wrap.addClass('x-trigger-wrap-focus');
8919             this.mimicing = true;
8920             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8921             if(this.monitorTab){
8922                 this.el.on("keydown", this.checkTab, this);
8923             }
8924         }
8925         */
8926     },
8927
8928     // private
8929     checkTab : function(e){
8930         if(e.getKey() == e.TAB){
8931             this.triggerBlur();
8932         }
8933     },
8934
8935     // private
8936     onBlur : function(){
8937         // do nothing
8938     },
8939
8940     // private
8941     mimicBlur : function(e, t){
8942         /*
8943         if(!this.wrap.contains(t) && this.validateBlur()){
8944             this.triggerBlur();
8945         }
8946         */
8947     },
8948
8949     // private
8950     triggerBlur : function(){
8951         this.mimicing = false;
8952         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8953         if(this.monitorTab){
8954             this.el.un("keydown", this.checkTab, this);
8955         }
8956         //this.wrap.removeClass('x-trigger-wrap-focus');
8957         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8958     },
8959
8960     // private
8961     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8962     validateBlur : function(e, t){
8963         return true;
8964     },
8965
8966     // private
8967     onDisable : function(){
8968         this.inputEl().dom.disabled = true;
8969         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8970         //if(this.wrap){
8971         //    this.wrap.addClass('x-item-disabled');
8972         //}
8973     },
8974
8975     // private
8976     onEnable : function(){
8977         this.inputEl().dom.disabled = false;
8978         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8979         //if(this.wrap){
8980         //    this.el.removeClass('x-item-disabled');
8981         //}
8982     },
8983
8984     // private
8985     onShow : function(){
8986         var ae = this.getActionEl();
8987         
8988         if(ae){
8989             ae.dom.style.display = '';
8990             ae.dom.style.visibility = 'visible';
8991         }
8992     },
8993
8994     // private
8995     
8996     onHide : function(){
8997         var ae = this.getActionEl();
8998         ae.dom.style.display = 'none';
8999     },
9000
9001     /**
9002      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9003      * by an implementing function.
9004      * @method
9005      * @param {EventObject} e
9006      */
9007     onTriggerClick : Roo.emptyFn
9008 });
9009  /*
9010  * Based on:
9011  * Ext JS Library 1.1.1
9012  * Copyright(c) 2006-2007, Ext JS, LLC.
9013  *
9014  * Originally Released Under LGPL - original licence link has changed is not relivant.
9015  *
9016  * Fork - LGPL
9017  * <script type="text/javascript">
9018  */
9019
9020
9021 /**
9022  * @class Roo.data.SortTypes
9023  * @singleton
9024  * Defines the default sorting (casting?) comparison functions used when sorting data.
9025  */
9026 Roo.data.SortTypes = {
9027     /**
9028      * Default sort that does nothing
9029      * @param {Mixed} s The value being converted
9030      * @return {Mixed} The comparison value
9031      */
9032     none : function(s){
9033         return s;
9034     },
9035     
9036     /**
9037      * The regular expression used to strip tags
9038      * @type {RegExp}
9039      * @property
9040      */
9041     stripTagsRE : /<\/?[^>]+>/gi,
9042     
9043     /**
9044      * Strips all HTML tags to sort on text only
9045      * @param {Mixed} s The value being converted
9046      * @return {String} The comparison value
9047      */
9048     asText : function(s){
9049         return String(s).replace(this.stripTagsRE, "");
9050     },
9051     
9052     /**
9053      * Strips all HTML tags to sort on text only - Case insensitive
9054      * @param {Mixed} s The value being converted
9055      * @return {String} The comparison value
9056      */
9057     asUCText : function(s){
9058         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9059     },
9060     
9061     /**
9062      * Case insensitive string
9063      * @param {Mixed} s The value being converted
9064      * @return {String} The comparison value
9065      */
9066     asUCString : function(s) {
9067         return String(s).toUpperCase();
9068     },
9069     
9070     /**
9071      * Date sorting
9072      * @param {Mixed} s The value being converted
9073      * @return {Number} The comparison value
9074      */
9075     asDate : function(s) {
9076         if(!s){
9077             return 0;
9078         }
9079         if(s instanceof Date){
9080             return s.getTime();
9081         }
9082         return Date.parse(String(s));
9083     },
9084     
9085     /**
9086      * Float sorting
9087      * @param {Mixed} s The value being converted
9088      * @return {Float} The comparison value
9089      */
9090     asFloat : function(s) {
9091         var val = parseFloat(String(s).replace(/,/g, ""));
9092         if(isNaN(val)) val = 0;
9093         return val;
9094     },
9095     
9096     /**
9097      * Integer sorting
9098      * @param {Mixed} s The value being converted
9099      * @return {Number} The comparison value
9100      */
9101     asInt : function(s) {
9102         var val = parseInt(String(s).replace(/,/g, ""));
9103         if(isNaN(val)) val = 0;
9104         return val;
9105     }
9106 };/*
9107  * Based on:
9108  * Ext JS Library 1.1.1
9109  * Copyright(c) 2006-2007, Ext JS, LLC.
9110  *
9111  * Originally Released Under LGPL - original licence link has changed is not relivant.
9112  *
9113  * Fork - LGPL
9114  * <script type="text/javascript">
9115  */
9116
9117 /**
9118 * @class Roo.data.Record
9119  * Instances of this class encapsulate both record <em>definition</em> information, and record
9120  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9121  * to access Records cached in an {@link Roo.data.Store} object.<br>
9122  * <p>
9123  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9124  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9125  * objects.<br>
9126  * <p>
9127  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9128  * @constructor
9129  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9130  * {@link #create}. The parameters are the same.
9131  * @param {Array} data An associative Array of data values keyed by the field name.
9132  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9133  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9134  * not specified an integer id is generated.
9135  */
9136 Roo.data.Record = function(data, id){
9137     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9138     this.data = data;
9139 };
9140
9141 /**
9142  * Generate a constructor for a specific record layout.
9143  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9144  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9145  * Each field definition object may contain the following properties: <ul>
9146  * <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,
9147  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9148  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9149  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9150  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9151  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9152  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9153  * this may be omitted.</p></li>
9154  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9155  * <ul><li>auto (Default, implies no conversion)</li>
9156  * <li>string</li>
9157  * <li>int</li>
9158  * <li>float</li>
9159  * <li>boolean</li>
9160  * <li>date</li></ul></p></li>
9161  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9162  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9163  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9164  * by the Reader into an object that will be stored in the Record. It is passed the
9165  * following parameters:<ul>
9166  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9167  * </ul></p></li>
9168  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9169  * </ul>
9170  * <br>usage:<br><pre><code>
9171 var TopicRecord = Roo.data.Record.create(
9172     {name: 'title', mapping: 'topic_title'},
9173     {name: 'author', mapping: 'username'},
9174     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9175     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9176     {name: 'lastPoster', mapping: 'user2'},
9177     {name: 'excerpt', mapping: 'post_text'}
9178 );
9179
9180 var myNewRecord = new TopicRecord({
9181     title: 'Do my job please',
9182     author: 'noobie',
9183     totalPosts: 1,
9184     lastPost: new Date(),
9185     lastPoster: 'Animal',
9186     excerpt: 'No way dude!'
9187 });
9188 myStore.add(myNewRecord);
9189 </code></pre>
9190  * @method create
9191  * @static
9192  */
9193 Roo.data.Record.create = function(o){
9194     var f = function(){
9195         f.superclass.constructor.apply(this, arguments);
9196     };
9197     Roo.extend(f, Roo.data.Record);
9198     var p = f.prototype;
9199     p.fields = new Roo.util.MixedCollection(false, function(field){
9200         return field.name;
9201     });
9202     for(var i = 0, len = o.length; i < len; i++){
9203         p.fields.add(new Roo.data.Field(o[i]));
9204     }
9205     f.getField = function(name){
9206         return p.fields.get(name);  
9207     };
9208     return f;
9209 };
9210
9211 Roo.data.Record.AUTO_ID = 1000;
9212 Roo.data.Record.EDIT = 'edit';
9213 Roo.data.Record.REJECT = 'reject';
9214 Roo.data.Record.COMMIT = 'commit';
9215
9216 Roo.data.Record.prototype = {
9217     /**
9218      * Readonly flag - true if this record has been modified.
9219      * @type Boolean
9220      */
9221     dirty : false,
9222     editing : false,
9223     error: null,
9224     modified: null,
9225
9226     // private
9227     join : function(store){
9228         this.store = store;
9229     },
9230
9231     /**
9232      * Set the named field to the specified value.
9233      * @param {String} name The name of the field to set.
9234      * @param {Object} value The value to set the field to.
9235      */
9236     set : function(name, value){
9237         if(this.data[name] == value){
9238             return;
9239         }
9240         this.dirty = true;
9241         if(!this.modified){
9242             this.modified = {};
9243         }
9244         if(typeof this.modified[name] == 'undefined'){
9245             this.modified[name] = this.data[name];
9246         }
9247         this.data[name] = value;
9248         if(!this.editing && this.store){
9249             this.store.afterEdit(this);
9250         }       
9251     },
9252
9253     /**
9254      * Get the value of the named field.
9255      * @param {String} name The name of the field to get the value of.
9256      * @return {Object} The value of the field.
9257      */
9258     get : function(name){
9259         return this.data[name]; 
9260     },
9261
9262     // private
9263     beginEdit : function(){
9264         this.editing = true;
9265         this.modified = {}; 
9266     },
9267
9268     // private
9269     cancelEdit : function(){
9270         this.editing = false;
9271         delete this.modified;
9272     },
9273
9274     // private
9275     endEdit : function(){
9276         this.editing = false;
9277         if(this.dirty && this.store){
9278             this.store.afterEdit(this);
9279         }
9280     },
9281
9282     /**
9283      * Usually called by the {@link Roo.data.Store} which owns the Record.
9284      * Rejects all changes made to the Record since either creation, or the last commit operation.
9285      * Modified fields are reverted to their original values.
9286      * <p>
9287      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9288      * of reject operations.
9289      */
9290     reject : function(){
9291         var m = this.modified;
9292         for(var n in m){
9293             if(typeof m[n] != "function"){
9294                 this.data[n] = m[n];
9295             }
9296         }
9297         this.dirty = false;
9298         delete this.modified;
9299         this.editing = false;
9300         if(this.store){
9301             this.store.afterReject(this);
9302         }
9303     },
9304
9305     /**
9306      * Usually called by the {@link Roo.data.Store} which owns the Record.
9307      * Commits all changes made to the Record since either creation, or the last commit operation.
9308      * <p>
9309      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9310      * of commit operations.
9311      */
9312     commit : function(){
9313         this.dirty = false;
9314         delete this.modified;
9315         this.editing = false;
9316         if(this.store){
9317             this.store.afterCommit(this);
9318         }
9319     },
9320
9321     // private
9322     hasError : function(){
9323         return this.error != null;
9324     },
9325
9326     // private
9327     clearError : function(){
9328         this.error = null;
9329     },
9330
9331     /**
9332      * Creates a copy of this record.
9333      * @param {String} id (optional) A new record id if you don't want to use this record's id
9334      * @return {Record}
9335      */
9336     copy : function(newId) {
9337         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9338     }
9339 };/*
9340  * Based on:
9341  * Ext JS Library 1.1.1
9342  * Copyright(c) 2006-2007, Ext JS, LLC.
9343  *
9344  * Originally Released Under LGPL - original licence link has changed is not relivant.
9345  *
9346  * Fork - LGPL
9347  * <script type="text/javascript">
9348  */
9349
9350
9351
9352 /**
9353  * @class Roo.data.Store
9354  * @extends Roo.util.Observable
9355  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9356  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9357  * <p>
9358  * 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
9359  * has no knowledge of the format of the data returned by the Proxy.<br>
9360  * <p>
9361  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9362  * instances from the data object. These records are cached and made available through accessor functions.
9363  * @constructor
9364  * Creates a new Store.
9365  * @param {Object} config A config object containing the objects needed for the Store to access data,
9366  * and read the data into Records.
9367  */
9368 Roo.data.Store = function(config){
9369     this.data = new Roo.util.MixedCollection(false);
9370     this.data.getKey = function(o){
9371         return o.id;
9372     };
9373     this.baseParams = {};
9374     // private
9375     this.paramNames = {
9376         "start" : "start",
9377         "limit" : "limit",
9378         "sort" : "sort",
9379         "dir" : "dir",
9380         "multisort" : "_multisort"
9381     };
9382
9383     if(config && config.data){
9384         this.inlineData = config.data;
9385         delete config.data;
9386     }
9387
9388     Roo.apply(this, config);
9389     
9390     if(this.reader){ // reader passed
9391         this.reader = Roo.factory(this.reader, Roo.data);
9392         this.reader.xmodule = this.xmodule || false;
9393         if(!this.recordType){
9394             this.recordType = this.reader.recordType;
9395         }
9396         if(this.reader.onMetaChange){
9397             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9398         }
9399     }
9400
9401     if(this.recordType){
9402         this.fields = this.recordType.prototype.fields;
9403     }
9404     this.modified = [];
9405
9406     this.addEvents({
9407         /**
9408          * @event datachanged
9409          * Fires when the data cache has changed, and a widget which is using this Store
9410          * as a Record cache should refresh its view.
9411          * @param {Store} this
9412          */
9413         datachanged : true,
9414         /**
9415          * @event metachange
9416          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9417          * @param {Store} this
9418          * @param {Object} meta The JSON metadata
9419          */
9420         metachange : true,
9421         /**
9422          * @event add
9423          * Fires when Records have been added to the Store
9424          * @param {Store} this
9425          * @param {Roo.data.Record[]} records The array of Records added
9426          * @param {Number} index The index at which the record(s) were added
9427          */
9428         add : true,
9429         /**
9430          * @event remove
9431          * Fires when a Record has been removed from the Store
9432          * @param {Store} this
9433          * @param {Roo.data.Record} record The Record that was removed
9434          * @param {Number} index The index at which the record was removed
9435          */
9436         remove : true,
9437         /**
9438          * @event update
9439          * Fires when a Record has been updated
9440          * @param {Store} this
9441          * @param {Roo.data.Record} record The Record that was updated
9442          * @param {String} operation The update operation being performed.  Value may be one of:
9443          * <pre><code>
9444  Roo.data.Record.EDIT
9445  Roo.data.Record.REJECT
9446  Roo.data.Record.COMMIT
9447          * </code></pre>
9448          */
9449         update : true,
9450         /**
9451          * @event clear
9452          * Fires when the data cache has been cleared.
9453          * @param {Store} this
9454          */
9455         clear : true,
9456         /**
9457          * @event beforeload
9458          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9459          * the load action will be canceled.
9460          * @param {Store} this
9461          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9462          */
9463         beforeload : true,
9464         /**
9465          * @event beforeloadadd
9466          * Fires after a new set of Records has been loaded.
9467          * @param {Store} this
9468          * @param {Roo.data.Record[]} records The Records that were loaded
9469          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9470          */
9471         beforeloadadd : true,
9472         /**
9473          * @event load
9474          * Fires after a new set of Records has been loaded, before they are added to the store.
9475          * @param {Store} this
9476          * @param {Roo.data.Record[]} records The Records that were loaded
9477          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9478          * @params {Object} return from reader
9479          */
9480         load : true,
9481         /**
9482          * @event loadexception
9483          * Fires if an exception occurs in the Proxy during loading.
9484          * Called with the signature of the Proxy's "loadexception" event.
9485          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9486          * 
9487          * @param {Proxy} 
9488          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9489          * @param {Object} load options 
9490          * @param {Object} jsonData from your request (normally this contains the Exception)
9491          */
9492         loadexception : true
9493     });
9494     
9495     if(this.proxy){
9496         this.proxy = Roo.factory(this.proxy, Roo.data);
9497         this.proxy.xmodule = this.xmodule || false;
9498         this.relayEvents(this.proxy,  ["loadexception"]);
9499     }
9500     this.sortToggle = {};
9501     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9502
9503     Roo.data.Store.superclass.constructor.call(this);
9504
9505     if(this.inlineData){
9506         this.loadData(this.inlineData);
9507         delete this.inlineData;
9508     }
9509 };
9510
9511 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9512      /**
9513     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9514     * without a remote query - used by combo/forms at present.
9515     */
9516     
9517     /**
9518     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9519     */
9520     /**
9521     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9522     */
9523     /**
9524     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9525     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9526     */
9527     /**
9528     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9529     * on any HTTP request
9530     */
9531     /**
9532     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9533     */
9534     /**
9535     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9536     */
9537     multiSort: false,
9538     /**
9539     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9540     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9541     */
9542     remoteSort : false,
9543
9544     /**
9545     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9546      * loaded or when a record is removed. (defaults to false).
9547     */
9548     pruneModifiedRecords : false,
9549
9550     // private
9551     lastOptions : null,
9552
9553     /**
9554      * Add Records to the Store and fires the add event.
9555      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9556      */
9557     add : function(records){
9558         records = [].concat(records);
9559         for(var i = 0, len = records.length; i < len; i++){
9560             records[i].join(this);
9561         }
9562         var index = this.data.length;
9563         this.data.addAll(records);
9564         this.fireEvent("add", this, records, index);
9565     },
9566
9567     /**
9568      * Remove a Record from the Store and fires the remove event.
9569      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9570      */
9571     remove : function(record){
9572         var index = this.data.indexOf(record);
9573         this.data.removeAt(index);
9574         if(this.pruneModifiedRecords){
9575             this.modified.remove(record);
9576         }
9577         this.fireEvent("remove", this, record, index);
9578     },
9579
9580     /**
9581      * Remove all Records from the Store and fires the clear event.
9582      */
9583     removeAll : function(){
9584         this.data.clear();
9585         if(this.pruneModifiedRecords){
9586             this.modified = [];
9587         }
9588         this.fireEvent("clear", this);
9589     },
9590
9591     /**
9592      * Inserts Records to the Store at the given index and fires the add event.
9593      * @param {Number} index The start index at which to insert the passed Records.
9594      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9595      */
9596     insert : function(index, records){
9597         records = [].concat(records);
9598         for(var i = 0, len = records.length; i < len; i++){
9599             this.data.insert(index, records[i]);
9600             records[i].join(this);
9601         }
9602         this.fireEvent("add", this, records, index);
9603     },
9604
9605     /**
9606      * Get the index within the cache of the passed Record.
9607      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9608      * @return {Number} The index of the passed Record. Returns -1 if not found.
9609      */
9610     indexOf : function(record){
9611         return this.data.indexOf(record);
9612     },
9613
9614     /**
9615      * Get the index within the cache of the Record with the passed id.
9616      * @param {String} id The id of the Record to find.
9617      * @return {Number} The index of the Record. Returns -1 if not found.
9618      */
9619     indexOfId : function(id){
9620         return this.data.indexOfKey(id);
9621     },
9622
9623     /**
9624      * Get the Record with the specified id.
9625      * @param {String} id The id of the Record to find.
9626      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9627      */
9628     getById : function(id){
9629         return this.data.key(id);
9630     },
9631
9632     /**
9633      * Get the Record at the specified index.
9634      * @param {Number} index The index of the Record to find.
9635      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9636      */
9637     getAt : function(index){
9638         return this.data.itemAt(index);
9639     },
9640
9641     /**
9642      * Returns a range of Records between specified indices.
9643      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9644      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9645      * @return {Roo.data.Record[]} An array of Records
9646      */
9647     getRange : function(start, end){
9648         return this.data.getRange(start, end);
9649     },
9650
9651     // private
9652     storeOptions : function(o){
9653         o = Roo.apply({}, o);
9654         delete o.callback;
9655         delete o.scope;
9656         this.lastOptions = o;
9657     },
9658
9659     /**
9660      * Loads the Record cache from the configured Proxy using the configured Reader.
9661      * <p>
9662      * If using remote paging, then the first load call must specify the <em>start</em>
9663      * and <em>limit</em> properties in the options.params property to establish the initial
9664      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9665      * <p>
9666      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9667      * and this call will return before the new data has been loaded. Perform any post-processing
9668      * in a callback function, or in a "load" event handler.</strong>
9669      * <p>
9670      * @param {Object} options An object containing properties which control loading options:<ul>
9671      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9672      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9673      * passed the following arguments:<ul>
9674      * <li>r : Roo.data.Record[]</li>
9675      * <li>options: Options object from the load call</li>
9676      * <li>success: Boolean success indicator</li></ul></li>
9677      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9678      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9679      * </ul>
9680      */
9681     load : function(options){
9682         options = options || {};
9683         if(this.fireEvent("beforeload", this, options) !== false){
9684             this.storeOptions(options);
9685             var p = Roo.apply(options.params || {}, this.baseParams);
9686             // if meta was not loaded from remote source.. try requesting it.
9687             if (!this.reader.metaFromRemote) {
9688                 p._requestMeta = 1;
9689             }
9690             if(this.sortInfo && this.remoteSort){
9691                 var pn = this.paramNames;
9692                 p[pn["sort"]] = this.sortInfo.field;
9693                 p[pn["dir"]] = this.sortInfo.direction;
9694             }
9695             if (this.multiSort) {
9696                 var pn = this.paramNames;
9697                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9698             }
9699             
9700             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9701         }
9702     },
9703
9704     /**
9705      * Reloads the Record cache from the configured Proxy using the configured Reader and
9706      * the options from the last load operation performed.
9707      * @param {Object} options (optional) An object containing properties which may override the options
9708      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9709      * the most recently used options are reused).
9710      */
9711     reload : function(options){
9712         this.load(Roo.applyIf(options||{}, this.lastOptions));
9713     },
9714
9715     // private
9716     // Called as a callback by the Reader during a load operation.
9717     loadRecords : function(o, options, success){
9718         if(!o || success === false){
9719             if(success !== false){
9720                 this.fireEvent("load", this, [], options, o);
9721             }
9722             if(options.callback){
9723                 options.callback.call(options.scope || this, [], options, false);
9724             }
9725             return;
9726         }
9727         // if data returned failure - throw an exception.
9728         if (o.success === false) {
9729             // show a message if no listener is registered.
9730             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9731                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9732             }
9733             // loadmask wil be hooked into this..
9734             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9735             return;
9736         }
9737         var r = o.records, t = o.totalRecords || r.length;
9738         
9739         this.fireEvent("beforeloadadd", this, r, options, o);
9740         
9741         if(!options || options.add !== true){
9742             if(this.pruneModifiedRecords){
9743                 this.modified = [];
9744             }
9745             for(var i = 0, len = r.length; i < len; i++){
9746                 r[i].join(this);
9747             }
9748             if(this.snapshot){
9749                 this.data = this.snapshot;
9750                 delete this.snapshot;
9751             }
9752             this.data.clear();
9753             this.data.addAll(r);
9754             this.totalLength = t;
9755             this.applySort();
9756             this.fireEvent("datachanged", this);
9757         }else{
9758             this.totalLength = Math.max(t, this.data.length+r.length);
9759             this.add(r);
9760         }
9761         this.fireEvent("load", this, r, options, o);
9762         if(options.callback){
9763             options.callback.call(options.scope || this, r, options, true);
9764         }
9765     },
9766
9767
9768     /**
9769      * Loads data from a passed data block. A Reader which understands the format of the data
9770      * must have been configured in the constructor.
9771      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9772      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9773      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9774      */
9775     loadData : function(o, append){
9776         var r = this.reader.readRecords(o);
9777         this.loadRecords(r, {add: append}, true);
9778     },
9779
9780     /**
9781      * Gets the number of cached records.
9782      * <p>
9783      * <em>If using paging, this may not be the total size of the dataset. If the data object
9784      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9785      * the data set size</em>
9786      */
9787     getCount : function(){
9788         return this.data.length || 0;
9789     },
9790
9791     /**
9792      * Gets the total number of records in the dataset as returned by the server.
9793      * <p>
9794      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9795      * the dataset size</em>
9796      */
9797     getTotalCount : function(){
9798         return this.totalLength || 0;
9799     },
9800
9801     /**
9802      * Returns the sort state of the Store as an object with two properties:
9803      * <pre><code>
9804  field {String} The name of the field by which the Records are sorted
9805  direction {String} The sort order, "ASC" or "DESC"
9806      * </code></pre>
9807      */
9808     getSortState : function(){
9809         return this.sortInfo;
9810     },
9811
9812     // private
9813     applySort : function(){
9814         if(this.sortInfo && !this.remoteSort){
9815             var s = this.sortInfo, f = s.field;
9816             var st = this.fields.get(f).sortType;
9817             var fn = function(r1, r2){
9818                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9819                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9820             };
9821             this.data.sort(s.direction, fn);
9822             if(this.snapshot && this.snapshot != this.data){
9823                 this.snapshot.sort(s.direction, fn);
9824             }
9825         }
9826     },
9827
9828     /**
9829      * Sets the default sort column and order to be used by the next load operation.
9830      * @param {String} fieldName The name of the field to sort by.
9831      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9832      */
9833     setDefaultSort : function(field, dir){
9834         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9835     },
9836
9837     /**
9838      * Sort the Records.
9839      * If remote sorting is used, the sort is performed on the server, and the cache is
9840      * reloaded. If local sorting is used, the cache is sorted internally.
9841      * @param {String} fieldName The name of the field to sort by.
9842      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9843      */
9844     sort : function(fieldName, dir){
9845         var f = this.fields.get(fieldName);
9846         if(!dir){
9847             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9848             
9849             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9850                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9851             }else{
9852                 dir = f.sortDir;
9853             }
9854         }
9855         this.sortToggle[f.name] = dir;
9856         this.sortInfo = {field: f.name, direction: dir};
9857         if(!this.remoteSort){
9858             this.applySort();
9859             this.fireEvent("datachanged", this);
9860         }else{
9861             this.load(this.lastOptions);
9862         }
9863     },
9864
9865     /**
9866      * Calls the specified function for each of the Records in the cache.
9867      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9868      * Returning <em>false</em> aborts and exits the iteration.
9869      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9870      */
9871     each : function(fn, scope){
9872         this.data.each(fn, scope);
9873     },
9874
9875     /**
9876      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9877      * (e.g., during paging).
9878      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9879      */
9880     getModifiedRecords : function(){
9881         return this.modified;
9882     },
9883
9884     // private
9885     createFilterFn : function(property, value, anyMatch){
9886         if(!value.exec){ // not a regex
9887             value = String(value);
9888             if(value.length == 0){
9889                 return false;
9890             }
9891             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9892         }
9893         return function(r){
9894             return value.test(r.data[property]);
9895         };
9896     },
9897
9898     /**
9899      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9900      * @param {String} property A field on your records
9901      * @param {Number} start The record index to start at (defaults to 0)
9902      * @param {Number} end The last record index to include (defaults to length - 1)
9903      * @return {Number} The sum
9904      */
9905     sum : function(property, start, end){
9906         var rs = this.data.items, v = 0;
9907         start = start || 0;
9908         end = (end || end === 0) ? end : rs.length-1;
9909
9910         for(var i = start; i <= end; i++){
9911             v += (rs[i].data[property] || 0);
9912         }
9913         return v;
9914     },
9915
9916     /**
9917      * Filter the records by a specified property.
9918      * @param {String} field A field on your records
9919      * @param {String/RegExp} value Either a string that the field
9920      * should start with or a RegExp to test against the field
9921      * @param {Boolean} anyMatch True to match any part not just the beginning
9922      */
9923     filter : function(property, value, anyMatch){
9924         var fn = this.createFilterFn(property, value, anyMatch);
9925         return fn ? this.filterBy(fn) : this.clearFilter();
9926     },
9927
9928     /**
9929      * Filter by a function. The specified function will be called with each
9930      * record in this data source. If the function returns true the record is included,
9931      * otherwise it is filtered.
9932      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9933      * @param {Object} scope (optional) The scope of the function (defaults to this)
9934      */
9935     filterBy : function(fn, scope){
9936         this.snapshot = this.snapshot || this.data;
9937         this.data = this.queryBy(fn, scope||this);
9938         this.fireEvent("datachanged", this);
9939     },
9940
9941     /**
9942      * Query the records by a specified property.
9943      * @param {String} field A field on your records
9944      * @param {String/RegExp} value Either a string that the field
9945      * should start with or a RegExp to test against the field
9946      * @param {Boolean} anyMatch True to match any part not just the beginning
9947      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9948      */
9949     query : function(property, value, anyMatch){
9950         var fn = this.createFilterFn(property, value, anyMatch);
9951         return fn ? this.queryBy(fn) : this.data.clone();
9952     },
9953
9954     /**
9955      * Query by a function. The specified function will be called with each
9956      * record in this data source. If the function returns true the record is included
9957      * in the results.
9958      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9959      * @param {Object} scope (optional) The scope of the function (defaults to this)
9960       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9961      **/
9962     queryBy : function(fn, scope){
9963         var data = this.snapshot || this.data;
9964         return data.filterBy(fn, scope||this);
9965     },
9966
9967     /**
9968      * Collects unique values for a particular dataIndex from this store.
9969      * @param {String} dataIndex The property to collect
9970      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9971      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9972      * @return {Array} An array of the unique values
9973      **/
9974     collect : function(dataIndex, allowNull, bypassFilter){
9975         var d = (bypassFilter === true && this.snapshot) ?
9976                 this.snapshot.items : this.data.items;
9977         var v, sv, r = [], l = {};
9978         for(var i = 0, len = d.length; i < len; i++){
9979             v = d[i].data[dataIndex];
9980             sv = String(v);
9981             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9982                 l[sv] = true;
9983                 r[r.length] = v;
9984             }
9985         }
9986         return r;
9987     },
9988
9989     /**
9990      * Revert to a view of the Record cache with no filtering applied.
9991      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9992      */
9993     clearFilter : function(suppressEvent){
9994         if(this.snapshot && this.snapshot != this.data){
9995             this.data = this.snapshot;
9996             delete this.snapshot;
9997             if(suppressEvent !== true){
9998                 this.fireEvent("datachanged", this);
9999             }
10000         }
10001     },
10002
10003     // private
10004     afterEdit : function(record){
10005         if(this.modified.indexOf(record) == -1){
10006             this.modified.push(record);
10007         }
10008         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10009     },
10010     
10011     // private
10012     afterReject : function(record){
10013         this.modified.remove(record);
10014         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10015     },
10016
10017     // private
10018     afterCommit : function(record){
10019         this.modified.remove(record);
10020         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10021     },
10022
10023     /**
10024      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10025      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10026      */
10027     commitChanges : function(){
10028         var m = this.modified.slice(0);
10029         this.modified = [];
10030         for(var i = 0, len = m.length; i < len; i++){
10031             m[i].commit();
10032         }
10033     },
10034
10035     /**
10036      * Cancel outstanding changes on all changed records.
10037      */
10038     rejectChanges : function(){
10039         var m = this.modified.slice(0);
10040         this.modified = [];
10041         for(var i = 0, len = m.length; i < len; i++){
10042             m[i].reject();
10043         }
10044     },
10045
10046     onMetaChange : function(meta, rtype, o){
10047         this.recordType = rtype;
10048         this.fields = rtype.prototype.fields;
10049         delete this.snapshot;
10050         this.sortInfo = meta.sortInfo || this.sortInfo;
10051         this.modified = [];
10052         this.fireEvent('metachange', this, this.reader.meta);
10053     },
10054     
10055     moveIndex : function(data, type)
10056     {
10057         var index = this.indexOf(data);
10058         
10059         var newIndex = index + type;
10060         
10061         this.remove(data);
10062         
10063         this.insert(newIndex, data);
10064         
10065     }
10066 });/*
10067  * Based on:
10068  * Ext JS Library 1.1.1
10069  * Copyright(c) 2006-2007, Ext JS, LLC.
10070  *
10071  * Originally Released Under LGPL - original licence link has changed is not relivant.
10072  *
10073  * Fork - LGPL
10074  * <script type="text/javascript">
10075  */
10076
10077 /**
10078  * @class Roo.data.SimpleStore
10079  * @extends Roo.data.Store
10080  * Small helper class to make creating Stores from Array data easier.
10081  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10082  * @cfg {Array} fields An array of field definition objects, or field name strings.
10083  * @cfg {Array} data The multi-dimensional array of data
10084  * @constructor
10085  * @param {Object} config
10086  */
10087 Roo.data.SimpleStore = function(config){
10088     Roo.data.SimpleStore.superclass.constructor.call(this, {
10089         isLocal : true,
10090         reader: new Roo.data.ArrayReader({
10091                 id: config.id
10092             },
10093             Roo.data.Record.create(config.fields)
10094         ),
10095         proxy : new Roo.data.MemoryProxy(config.data)
10096     });
10097     this.load();
10098 };
10099 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10100  * Based on:
10101  * Ext JS Library 1.1.1
10102  * Copyright(c) 2006-2007, Ext JS, LLC.
10103  *
10104  * Originally Released Under LGPL - original licence link has changed is not relivant.
10105  *
10106  * Fork - LGPL
10107  * <script type="text/javascript">
10108  */
10109
10110 /**
10111 /**
10112  * @extends Roo.data.Store
10113  * @class Roo.data.JsonStore
10114  * Small helper class to make creating Stores for JSON data easier. <br/>
10115 <pre><code>
10116 var store = new Roo.data.JsonStore({
10117     url: 'get-images.php',
10118     root: 'images',
10119     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10120 });
10121 </code></pre>
10122  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10123  * JsonReader and HttpProxy (unless inline data is provided).</b>
10124  * @cfg {Array} fields An array of field definition objects, or field name strings.
10125  * @constructor
10126  * @param {Object} config
10127  */
10128 Roo.data.JsonStore = function(c){
10129     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10130         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10131         reader: new Roo.data.JsonReader(c, c.fields)
10132     }));
10133 };
10134 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10135  * Based on:
10136  * Ext JS Library 1.1.1
10137  * Copyright(c) 2006-2007, Ext JS, LLC.
10138  *
10139  * Originally Released Under LGPL - original licence link has changed is not relivant.
10140  *
10141  * Fork - LGPL
10142  * <script type="text/javascript">
10143  */
10144
10145  
10146 Roo.data.Field = function(config){
10147     if(typeof config == "string"){
10148         config = {name: config};
10149     }
10150     Roo.apply(this, config);
10151     
10152     if(!this.type){
10153         this.type = "auto";
10154     }
10155     
10156     var st = Roo.data.SortTypes;
10157     // named sortTypes are supported, here we look them up
10158     if(typeof this.sortType == "string"){
10159         this.sortType = st[this.sortType];
10160     }
10161     
10162     // set default sortType for strings and dates
10163     if(!this.sortType){
10164         switch(this.type){
10165             case "string":
10166                 this.sortType = st.asUCString;
10167                 break;
10168             case "date":
10169                 this.sortType = st.asDate;
10170                 break;
10171             default:
10172                 this.sortType = st.none;
10173         }
10174     }
10175
10176     // define once
10177     var stripRe = /[\$,%]/g;
10178
10179     // prebuilt conversion function for this field, instead of
10180     // switching every time we're reading a value
10181     if(!this.convert){
10182         var cv, dateFormat = this.dateFormat;
10183         switch(this.type){
10184             case "":
10185             case "auto":
10186             case undefined:
10187                 cv = function(v){ return v; };
10188                 break;
10189             case "string":
10190                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10191                 break;
10192             case "int":
10193                 cv = function(v){
10194                     return v !== undefined && v !== null && v !== '' ?
10195                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10196                     };
10197                 break;
10198             case "float":
10199                 cv = function(v){
10200                     return v !== undefined && v !== null && v !== '' ?
10201                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10202                     };
10203                 break;
10204             case "bool":
10205             case "boolean":
10206                 cv = function(v){ return v === true || v === "true" || v == 1; };
10207                 break;
10208             case "date":
10209                 cv = function(v){
10210                     if(!v){
10211                         return '';
10212                     }
10213                     if(v instanceof Date){
10214                         return v;
10215                     }
10216                     if(dateFormat){
10217                         if(dateFormat == "timestamp"){
10218                             return new Date(v*1000);
10219                         }
10220                         return Date.parseDate(v, dateFormat);
10221                     }
10222                     var parsed = Date.parse(v);
10223                     return parsed ? new Date(parsed) : null;
10224                 };
10225              break;
10226             
10227         }
10228         this.convert = cv;
10229     }
10230 };
10231
10232 Roo.data.Field.prototype = {
10233     dateFormat: null,
10234     defaultValue: "",
10235     mapping: null,
10236     sortType : null,
10237     sortDir : "ASC"
10238 };/*
10239  * Based on:
10240  * Ext JS Library 1.1.1
10241  * Copyright(c) 2006-2007, Ext JS, LLC.
10242  *
10243  * Originally Released Under LGPL - original licence link has changed is not relivant.
10244  *
10245  * Fork - LGPL
10246  * <script type="text/javascript">
10247  */
10248  
10249 // Base class for reading structured data from a data source.  This class is intended to be
10250 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10251
10252 /**
10253  * @class Roo.data.DataReader
10254  * Base class for reading structured data from a data source.  This class is intended to be
10255  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10256  */
10257
10258 Roo.data.DataReader = function(meta, recordType){
10259     
10260     this.meta = meta;
10261     
10262     this.recordType = recordType instanceof Array ? 
10263         Roo.data.Record.create(recordType) : recordType;
10264 };
10265
10266 Roo.data.DataReader.prototype = {
10267      /**
10268      * Create an empty record
10269      * @param {Object} data (optional) - overlay some values
10270      * @return {Roo.data.Record} record created.
10271      */
10272     newRow :  function(d) {
10273         var da =  {};
10274         this.recordType.prototype.fields.each(function(c) {
10275             switch( c.type) {
10276                 case 'int' : da[c.name] = 0; break;
10277                 case 'date' : da[c.name] = new Date(); break;
10278                 case 'float' : da[c.name] = 0.0; break;
10279                 case 'boolean' : da[c.name] = false; break;
10280                 default : da[c.name] = ""; break;
10281             }
10282             
10283         });
10284         return new this.recordType(Roo.apply(da, d));
10285     }
10286     
10287 };/*
10288  * Based on:
10289  * Ext JS Library 1.1.1
10290  * Copyright(c) 2006-2007, Ext JS, LLC.
10291  *
10292  * Originally Released Under LGPL - original licence link has changed is not relivant.
10293  *
10294  * Fork - LGPL
10295  * <script type="text/javascript">
10296  */
10297
10298 /**
10299  * @class Roo.data.DataProxy
10300  * @extends Roo.data.Observable
10301  * This class is an abstract base class for implementations which provide retrieval of
10302  * unformatted data objects.<br>
10303  * <p>
10304  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10305  * (of the appropriate type which knows how to parse the data object) to provide a block of
10306  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10307  * <p>
10308  * Custom implementations must implement the load method as described in
10309  * {@link Roo.data.HttpProxy#load}.
10310  */
10311 Roo.data.DataProxy = function(){
10312     this.addEvents({
10313         /**
10314          * @event beforeload
10315          * Fires before a network request is made to retrieve a data object.
10316          * @param {Object} This DataProxy object.
10317          * @param {Object} params The params parameter to the load function.
10318          */
10319         beforeload : true,
10320         /**
10321          * @event load
10322          * Fires before the load method's callback is called.
10323          * @param {Object} This DataProxy object.
10324          * @param {Object} o The data object.
10325          * @param {Object} arg The callback argument object passed to the load function.
10326          */
10327         load : true,
10328         /**
10329          * @event loadexception
10330          * Fires if an Exception occurs during data retrieval.
10331          * @param {Object} This DataProxy object.
10332          * @param {Object} o The data object.
10333          * @param {Object} arg The callback argument object passed to the load function.
10334          * @param {Object} e The Exception.
10335          */
10336         loadexception : true
10337     });
10338     Roo.data.DataProxy.superclass.constructor.call(this);
10339 };
10340
10341 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10342
10343     /**
10344      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10345      */
10346 /*
10347  * Based on:
10348  * Ext JS Library 1.1.1
10349  * Copyright(c) 2006-2007, Ext JS, LLC.
10350  *
10351  * Originally Released Under LGPL - original licence link has changed is not relivant.
10352  *
10353  * Fork - LGPL
10354  * <script type="text/javascript">
10355  */
10356 /**
10357  * @class Roo.data.MemoryProxy
10358  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10359  * to the Reader when its load method is called.
10360  * @constructor
10361  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10362  */
10363 Roo.data.MemoryProxy = function(data){
10364     if (data.data) {
10365         data = data.data;
10366     }
10367     Roo.data.MemoryProxy.superclass.constructor.call(this);
10368     this.data = data;
10369 };
10370
10371 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10372     /**
10373      * Load data from the requested source (in this case an in-memory
10374      * data object passed to the constructor), read the data object into
10375      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10376      * process that block using the passed callback.
10377      * @param {Object} params This parameter is not used by the MemoryProxy class.
10378      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10379      * object into a block of Roo.data.Records.
10380      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10381      * The function must be passed <ul>
10382      * <li>The Record block object</li>
10383      * <li>The "arg" argument from the load function</li>
10384      * <li>A boolean success indicator</li>
10385      * </ul>
10386      * @param {Object} scope The scope in which to call the callback
10387      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10388      */
10389     load : function(params, reader, callback, scope, arg){
10390         params = params || {};
10391         var result;
10392         try {
10393             result = reader.readRecords(this.data);
10394         }catch(e){
10395             this.fireEvent("loadexception", this, arg, null, e);
10396             callback.call(scope, null, arg, false);
10397             return;
10398         }
10399         callback.call(scope, result, arg, true);
10400     },
10401     
10402     // private
10403     update : function(params, records){
10404         
10405     }
10406 });/*
10407  * Based on:
10408  * Ext JS Library 1.1.1
10409  * Copyright(c) 2006-2007, Ext JS, LLC.
10410  *
10411  * Originally Released Under LGPL - original licence link has changed is not relivant.
10412  *
10413  * Fork - LGPL
10414  * <script type="text/javascript">
10415  */
10416 /**
10417  * @class Roo.data.HttpProxy
10418  * @extends Roo.data.DataProxy
10419  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10420  * configured to reference a certain URL.<br><br>
10421  * <p>
10422  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10423  * from which the running page was served.<br><br>
10424  * <p>
10425  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10426  * <p>
10427  * Be aware that to enable the browser to parse an XML document, the server must set
10428  * the Content-Type header in the HTTP response to "text/xml".
10429  * @constructor
10430  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10431  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10432  * will be used to make the request.
10433  */
10434 Roo.data.HttpProxy = function(conn){
10435     Roo.data.HttpProxy.superclass.constructor.call(this);
10436     // is conn a conn config or a real conn?
10437     this.conn = conn;
10438     this.useAjax = !conn || !conn.events;
10439   
10440 };
10441
10442 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10443     // thse are take from connection...
10444     
10445     /**
10446      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10447      */
10448     /**
10449      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10450      * extra parameters to each request made by this object. (defaults to undefined)
10451      */
10452     /**
10453      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10454      *  to each request made by this object. (defaults to undefined)
10455      */
10456     /**
10457      * @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)
10458      */
10459     /**
10460      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10461      */
10462      /**
10463      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10464      * @type Boolean
10465      */
10466   
10467
10468     /**
10469      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10470      * @type Boolean
10471      */
10472     /**
10473      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10474      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10475      * a finer-grained basis than the DataProxy events.
10476      */
10477     getConnection : function(){
10478         return this.useAjax ? Roo.Ajax : this.conn;
10479     },
10480
10481     /**
10482      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10483      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10484      * process that block using the passed callback.
10485      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10486      * for the request to the remote server.
10487      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10488      * object into a block of Roo.data.Records.
10489      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10490      * The function must be passed <ul>
10491      * <li>The Record block object</li>
10492      * <li>The "arg" argument from the load function</li>
10493      * <li>A boolean success indicator</li>
10494      * </ul>
10495      * @param {Object} scope The scope in which to call the callback
10496      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10497      */
10498     load : function(params, reader, callback, scope, arg){
10499         if(this.fireEvent("beforeload", this, params) !== false){
10500             var  o = {
10501                 params : params || {},
10502                 request: {
10503                     callback : callback,
10504                     scope : scope,
10505                     arg : arg
10506                 },
10507                 reader: reader,
10508                 callback : this.loadResponse,
10509                 scope: this
10510             };
10511             if(this.useAjax){
10512                 Roo.applyIf(o, this.conn);
10513                 if(this.activeRequest){
10514                     Roo.Ajax.abort(this.activeRequest);
10515                 }
10516                 this.activeRequest = Roo.Ajax.request(o);
10517             }else{
10518                 this.conn.request(o);
10519             }
10520         }else{
10521             callback.call(scope||this, null, arg, false);
10522         }
10523     },
10524
10525     // private
10526     loadResponse : function(o, success, response){
10527         delete this.activeRequest;
10528         if(!success){
10529             this.fireEvent("loadexception", this, o, response);
10530             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10531             return;
10532         }
10533         var result;
10534         try {
10535             result = o.reader.read(response);
10536         }catch(e){
10537             this.fireEvent("loadexception", this, o, response, e);
10538             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10539             return;
10540         }
10541         
10542         this.fireEvent("load", this, o, o.request.arg);
10543         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10544     },
10545
10546     // private
10547     update : function(dataSet){
10548
10549     },
10550
10551     // private
10552     updateResponse : function(dataSet){
10553
10554     }
10555 });/*
10556  * Based on:
10557  * Ext JS Library 1.1.1
10558  * Copyright(c) 2006-2007, Ext JS, LLC.
10559  *
10560  * Originally Released Under LGPL - original licence link has changed is not relivant.
10561  *
10562  * Fork - LGPL
10563  * <script type="text/javascript">
10564  */
10565
10566 /**
10567  * @class Roo.data.ScriptTagProxy
10568  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10569  * other than the originating domain of the running page.<br><br>
10570  * <p>
10571  * <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
10572  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10573  * <p>
10574  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10575  * source code that is used as the source inside a &lt;script> tag.<br><br>
10576  * <p>
10577  * In order for the browser to process the returned data, the server must wrap the data object
10578  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10579  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10580  * depending on whether the callback name was passed:
10581  * <p>
10582  * <pre><code>
10583 boolean scriptTag = false;
10584 String cb = request.getParameter("callback");
10585 if (cb != null) {
10586     scriptTag = true;
10587     response.setContentType("text/javascript");
10588 } else {
10589     response.setContentType("application/x-json");
10590 }
10591 Writer out = response.getWriter();
10592 if (scriptTag) {
10593     out.write(cb + "(");
10594 }
10595 out.print(dataBlock.toJsonString());
10596 if (scriptTag) {
10597     out.write(");");
10598 }
10599 </pre></code>
10600  *
10601  * @constructor
10602  * @param {Object} config A configuration object.
10603  */
10604 Roo.data.ScriptTagProxy = function(config){
10605     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10606     Roo.apply(this, config);
10607     this.head = document.getElementsByTagName("head")[0];
10608 };
10609
10610 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10611
10612 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10613     /**
10614      * @cfg {String} url The URL from which to request the data object.
10615      */
10616     /**
10617      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10618      */
10619     timeout : 30000,
10620     /**
10621      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10622      * the server the name of the callback function set up by the load call to process the returned data object.
10623      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10624      * javascript output which calls this named function passing the data object as its only parameter.
10625      */
10626     callbackParam : "callback",
10627     /**
10628      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10629      * name to the request.
10630      */
10631     nocache : true,
10632
10633     /**
10634      * Load data from the configured URL, read the data object into
10635      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10636      * process that block using the passed callback.
10637      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10638      * for the request to the remote server.
10639      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10640      * object into a block of Roo.data.Records.
10641      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10642      * The function must be passed <ul>
10643      * <li>The Record block object</li>
10644      * <li>The "arg" argument from the load function</li>
10645      * <li>A boolean success indicator</li>
10646      * </ul>
10647      * @param {Object} scope The scope in which to call the callback
10648      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10649      */
10650     load : function(params, reader, callback, scope, arg){
10651         if(this.fireEvent("beforeload", this, params) !== false){
10652
10653             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10654
10655             var url = this.url;
10656             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10657             if(this.nocache){
10658                 url += "&_dc=" + (new Date().getTime());
10659             }
10660             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10661             var trans = {
10662                 id : transId,
10663                 cb : "stcCallback"+transId,
10664                 scriptId : "stcScript"+transId,
10665                 params : params,
10666                 arg : arg,
10667                 url : url,
10668                 callback : callback,
10669                 scope : scope,
10670                 reader : reader
10671             };
10672             var conn = this;
10673
10674             window[trans.cb] = function(o){
10675                 conn.handleResponse(o, trans);
10676             };
10677
10678             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10679
10680             if(this.autoAbort !== false){
10681                 this.abort();
10682             }
10683
10684             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10685
10686             var script = document.createElement("script");
10687             script.setAttribute("src", url);
10688             script.setAttribute("type", "text/javascript");
10689             script.setAttribute("id", trans.scriptId);
10690             this.head.appendChild(script);
10691
10692             this.trans = trans;
10693         }else{
10694             callback.call(scope||this, null, arg, false);
10695         }
10696     },
10697
10698     // private
10699     isLoading : function(){
10700         return this.trans ? true : false;
10701     },
10702
10703     /**
10704      * Abort the current server request.
10705      */
10706     abort : function(){
10707         if(this.isLoading()){
10708             this.destroyTrans(this.trans);
10709         }
10710     },
10711
10712     // private
10713     destroyTrans : function(trans, isLoaded){
10714         this.head.removeChild(document.getElementById(trans.scriptId));
10715         clearTimeout(trans.timeoutId);
10716         if(isLoaded){
10717             window[trans.cb] = undefined;
10718             try{
10719                 delete window[trans.cb];
10720             }catch(e){}
10721         }else{
10722             // if hasn't been loaded, wait for load to remove it to prevent script error
10723             window[trans.cb] = function(){
10724                 window[trans.cb] = undefined;
10725                 try{
10726                     delete window[trans.cb];
10727                 }catch(e){}
10728             };
10729         }
10730     },
10731
10732     // private
10733     handleResponse : function(o, trans){
10734         this.trans = false;
10735         this.destroyTrans(trans, true);
10736         var result;
10737         try {
10738             result = trans.reader.readRecords(o);
10739         }catch(e){
10740             this.fireEvent("loadexception", this, o, trans.arg, e);
10741             trans.callback.call(trans.scope||window, null, trans.arg, false);
10742             return;
10743         }
10744         this.fireEvent("load", this, o, trans.arg);
10745         trans.callback.call(trans.scope||window, result, trans.arg, true);
10746     },
10747
10748     // private
10749     handleFailure : function(trans){
10750         this.trans = false;
10751         this.destroyTrans(trans, false);
10752         this.fireEvent("loadexception", this, null, trans.arg);
10753         trans.callback.call(trans.scope||window, null, trans.arg, false);
10754     }
10755 });/*
10756  * Based on:
10757  * Ext JS Library 1.1.1
10758  * Copyright(c) 2006-2007, Ext JS, LLC.
10759  *
10760  * Originally Released Under LGPL - original licence link has changed is not relivant.
10761  *
10762  * Fork - LGPL
10763  * <script type="text/javascript">
10764  */
10765
10766 /**
10767  * @class Roo.data.JsonReader
10768  * @extends Roo.data.DataReader
10769  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10770  * based on mappings in a provided Roo.data.Record constructor.
10771  * 
10772  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10773  * in the reply previously. 
10774  * 
10775  * <p>
10776  * Example code:
10777  * <pre><code>
10778 var RecordDef = Roo.data.Record.create([
10779     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10780     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10781 ]);
10782 var myReader = new Roo.data.JsonReader({
10783     totalProperty: "results",    // The property which contains the total dataset size (optional)
10784     root: "rows",                // The property which contains an Array of row objects
10785     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10786 }, RecordDef);
10787 </code></pre>
10788  * <p>
10789  * This would consume a JSON file like this:
10790  * <pre><code>
10791 { 'results': 2, 'rows': [
10792     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10793     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10794 }
10795 </code></pre>
10796  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10797  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10798  * paged from the remote server.
10799  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10800  * @cfg {String} root name of the property which contains the Array of row objects.
10801  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10802  * @cfg {Array} fields Array of field definition objects
10803  * @constructor
10804  * Create a new JsonReader
10805  * @param {Object} meta Metadata configuration options
10806  * @param {Object} recordType Either an Array of field definition objects,
10807  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10808  */
10809 Roo.data.JsonReader = function(meta, recordType){
10810     
10811     meta = meta || {};
10812     // set some defaults:
10813     Roo.applyIf(meta, {
10814         totalProperty: 'total',
10815         successProperty : 'success',
10816         root : 'data',
10817         id : 'id'
10818     });
10819     
10820     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10821 };
10822 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10823     
10824     /**
10825      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10826      * Used by Store query builder to append _requestMeta to params.
10827      * 
10828      */
10829     metaFromRemote : false,
10830     /**
10831      * This method is only used by a DataProxy which has retrieved data from a remote server.
10832      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10833      * @return {Object} data A data block which is used by an Roo.data.Store object as
10834      * a cache of Roo.data.Records.
10835      */
10836     read : function(response){
10837         var json = response.responseText;
10838        
10839         var o = /* eval:var:o */ eval("("+json+")");
10840         if(!o) {
10841             throw {message: "JsonReader.read: Json object not found"};
10842         }
10843         
10844         if(o.metaData){
10845             
10846             delete this.ef;
10847             this.metaFromRemote = true;
10848             this.meta = o.metaData;
10849             this.recordType = Roo.data.Record.create(o.metaData.fields);
10850             this.onMetaChange(this.meta, this.recordType, o);
10851         }
10852         return this.readRecords(o);
10853     },
10854
10855     // private function a store will implement
10856     onMetaChange : function(meta, recordType, o){
10857
10858     },
10859
10860     /**
10861          * @ignore
10862          */
10863     simpleAccess: function(obj, subsc) {
10864         return obj[subsc];
10865     },
10866
10867         /**
10868          * @ignore
10869          */
10870     getJsonAccessor: function(){
10871         var re = /[\[\.]/;
10872         return function(expr) {
10873             try {
10874                 return(re.test(expr))
10875                     ? new Function("obj", "return obj." + expr)
10876                     : function(obj){
10877                         return obj[expr];
10878                     };
10879             } catch(e){}
10880             return Roo.emptyFn;
10881         };
10882     }(),
10883
10884     /**
10885      * Create a data block containing Roo.data.Records from an XML document.
10886      * @param {Object} o An object which contains an Array of row objects in the property specified
10887      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10888      * which contains the total size of the dataset.
10889      * @return {Object} data A data block which is used by an Roo.data.Store object as
10890      * a cache of Roo.data.Records.
10891      */
10892     readRecords : function(o){
10893         /**
10894          * After any data loads, the raw JSON data is available for further custom processing.
10895          * @type Object
10896          */
10897         this.o = o;
10898         var s = this.meta, Record = this.recordType,
10899             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10900
10901 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10902         if (!this.ef) {
10903             if(s.totalProperty) {
10904                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10905                 }
10906                 if(s.successProperty) {
10907                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10908                 }
10909                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10910                 if (s.id) {
10911                         var g = this.getJsonAccessor(s.id);
10912                         this.getId = function(rec) {
10913                                 var r = g(rec);  
10914                                 return (r === undefined || r === "") ? null : r;
10915                         };
10916                 } else {
10917                         this.getId = function(){return null;};
10918                 }
10919             this.ef = [];
10920             for(var jj = 0; jj < fl; jj++){
10921                 f = fi[jj];
10922                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10923                 this.ef[jj] = this.getJsonAccessor(map);
10924             }
10925         }
10926
10927         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10928         if(s.totalProperty){
10929             var vt = parseInt(this.getTotal(o), 10);
10930             if(!isNaN(vt)){
10931                 totalRecords = vt;
10932             }
10933         }
10934         if(s.successProperty){
10935             var vs = this.getSuccess(o);
10936             if(vs === false || vs === 'false'){
10937                 success = false;
10938             }
10939         }
10940         var records = [];
10941         for(var i = 0; i < c; i++){
10942                 var n = root[i];
10943             var values = {};
10944             var id = this.getId(n);
10945             for(var j = 0; j < fl; j++){
10946                 f = fi[j];
10947             var v = this.ef[j](n);
10948             if (!f.convert) {
10949                 Roo.log('missing convert for ' + f.name);
10950                 Roo.log(f);
10951                 continue;
10952             }
10953             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10954             }
10955             var record = new Record(values, id);
10956             record.json = n;
10957             records[i] = record;
10958         }
10959         return {
10960             raw : o,
10961             success : success,
10962             records : records,
10963             totalRecords : totalRecords
10964         };
10965     }
10966 });/*
10967  * Based on:
10968  * Ext JS Library 1.1.1
10969  * Copyright(c) 2006-2007, Ext JS, LLC.
10970  *
10971  * Originally Released Under LGPL - original licence link has changed is not relivant.
10972  *
10973  * Fork - LGPL
10974  * <script type="text/javascript">
10975  */
10976
10977 /**
10978  * @class Roo.data.ArrayReader
10979  * @extends Roo.data.DataReader
10980  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10981  * Each element of that Array represents a row of data fields. The
10982  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10983  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10984  * <p>
10985  * Example code:.
10986  * <pre><code>
10987 var RecordDef = Roo.data.Record.create([
10988     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10989     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10990 ]);
10991 var myReader = new Roo.data.ArrayReader({
10992     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10993 }, RecordDef);
10994 </code></pre>
10995  * <p>
10996  * This would consume an Array like this:
10997  * <pre><code>
10998 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10999   </code></pre>
11000  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11001  * @constructor
11002  * Create a new JsonReader
11003  * @param {Object} meta Metadata configuration options.
11004  * @param {Object} recordType Either an Array of field definition objects
11005  * as specified to {@link Roo.data.Record#create},
11006  * or an {@link Roo.data.Record} object
11007  * created using {@link Roo.data.Record#create}.
11008  */
11009 Roo.data.ArrayReader = function(meta, recordType){
11010     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11011 };
11012
11013 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11014     /**
11015      * Create a data block containing Roo.data.Records from an XML document.
11016      * @param {Object} o An Array of row objects which represents the dataset.
11017      * @return {Object} data A data block which is used by an Roo.data.Store object as
11018      * a cache of Roo.data.Records.
11019      */
11020     readRecords : function(o){
11021         var sid = this.meta ? this.meta.id : null;
11022         var recordType = this.recordType, fields = recordType.prototype.fields;
11023         var records = [];
11024         var root = o;
11025             for(var i = 0; i < root.length; i++){
11026                     var n = root[i];
11027                 var values = {};
11028                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11029                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11030                 var f = fields.items[j];
11031                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11032                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11033                 v = f.convert(v);
11034                 values[f.name] = v;
11035             }
11036                 var record = new recordType(values, id);
11037                 record.json = n;
11038                 records[records.length] = record;
11039             }
11040             return {
11041                 records : records,
11042                 totalRecords : records.length
11043             };
11044     }
11045 });/*
11046  * - LGPL
11047  * * 
11048  */
11049
11050 /**
11051  * @class Roo.bootstrap.ComboBox
11052  * @extends Roo.bootstrap.TriggerField
11053  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11054  * @cfg {Boolean} append (true|false) default false
11055  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11056  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11057  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11058  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11059  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11060  * @cfg {Boolean} animate default true
11061  * @cfg {Boolean} emptyResultText only for touch device
11062  * @constructor
11063  * Create a new ComboBox.
11064  * @param {Object} config Configuration options
11065  */
11066 Roo.bootstrap.ComboBox = function(config){
11067     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11068     this.addEvents({
11069         /**
11070          * @event expand
11071          * Fires when the dropdown list is expanded
11072              * @param {Roo.bootstrap.ComboBox} combo This combo box
11073              */
11074         'expand' : true,
11075         /**
11076          * @event collapse
11077          * Fires when the dropdown list is collapsed
11078              * @param {Roo.bootstrap.ComboBox} combo This combo box
11079              */
11080         'collapse' : true,
11081         /**
11082          * @event beforeselect
11083          * Fires before a list item is selected. Return false to cancel the selection.
11084              * @param {Roo.bootstrap.ComboBox} combo This combo box
11085              * @param {Roo.data.Record} record The data record returned from the underlying store
11086              * @param {Number} index The index of the selected item in the dropdown list
11087              */
11088         'beforeselect' : true,
11089         /**
11090          * @event select
11091          * Fires when a list item is selected
11092              * @param {Roo.bootstrap.ComboBox} combo This combo box
11093              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11094              * @param {Number} index The index of the selected item in the dropdown list
11095              */
11096         'select' : true,
11097         /**
11098          * @event beforequery
11099          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11100          * The event object passed has these properties:
11101              * @param {Roo.bootstrap.ComboBox} combo This combo box
11102              * @param {String} query The query
11103              * @param {Boolean} forceAll true to force "all" query
11104              * @param {Boolean} cancel true to cancel the query
11105              * @param {Object} e The query event object
11106              */
11107         'beforequery': true,
11108          /**
11109          * @event add
11110          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11111              * @param {Roo.bootstrap.ComboBox} combo This combo box
11112              */
11113         'add' : true,
11114         /**
11115          * @event edit
11116          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11117              * @param {Roo.bootstrap.ComboBox} combo This combo box
11118              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11119              */
11120         'edit' : true,
11121         /**
11122          * @event remove
11123          * Fires when the remove value from the combobox array
11124              * @param {Roo.bootstrap.ComboBox} combo This combo box
11125              */
11126         'remove' : true,
11127         /**
11128          * @event specialfilter
11129          * Fires when specialfilter
11130             * @param {Roo.bootstrap.ComboBox} combo This combo box
11131             */
11132         'specialfilter' : true
11133         
11134     });
11135     
11136     this.item = [];
11137     this.tickItems = [];
11138     
11139     this.selectedIndex = -1;
11140     if(this.mode == 'local'){
11141         if(config.queryDelay === undefined){
11142             this.queryDelay = 10;
11143         }
11144         if(config.minChars === undefined){
11145             this.minChars = 0;
11146         }
11147     }
11148 };
11149
11150 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11151      
11152     /**
11153      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11154      * rendering into an Roo.Editor, defaults to false)
11155      */
11156     /**
11157      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11158      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11159      */
11160     /**
11161      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11162      */
11163     /**
11164      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11165      * the dropdown list (defaults to undefined, with no header element)
11166      */
11167
11168      /**
11169      * @cfg {String/Roo.Template} tpl The template to use to render the output
11170      */
11171      
11172      /**
11173      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11174      */
11175     listWidth: undefined,
11176     /**
11177      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11178      * mode = 'remote' or 'text' if mode = 'local')
11179      */
11180     displayField: undefined,
11181     
11182     /**
11183      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11184      * mode = 'remote' or 'value' if mode = 'local'). 
11185      * Note: use of a valueField requires the user make a selection
11186      * in order for a value to be mapped.
11187      */
11188     valueField: undefined,
11189     
11190     
11191     /**
11192      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11193      * field's data value (defaults to the underlying DOM element's name)
11194      */
11195     hiddenName: undefined,
11196     /**
11197      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11198      */
11199     listClass: '',
11200     /**
11201      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11202      */
11203     selectedClass: 'active',
11204     
11205     /**
11206      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11207      */
11208     shadow:'sides',
11209     /**
11210      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11211      * anchor positions (defaults to 'tl-bl')
11212      */
11213     listAlign: 'tl-bl?',
11214     /**
11215      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11216      */
11217     maxHeight: 300,
11218     /**
11219      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11220      * query specified by the allQuery config option (defaults to 'query')
11221      */
11222     triggerAction: 'query',
11223     /**
11224      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11225      * (defaults to 4, does not apply if editable = false)
11226      */
11227     minChars : 4,
11228     /**
11229      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11230      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11231      */
11232     typeAhead: false,
11233     /**
11234      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11235      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11236      */
11237     queryDelay: 500,
11238     /**
11239      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11240      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11241      */
11242     pageSize: 0,
11243     /**
11244      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11245      * when editable = true (defaults to false)
11246      */
11247     selectOnFocus:false,
11248     /**
11249      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11250      */
11251     queryParam: 'query',
11252     /**
11253      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11254      * when mode = 'remote' (defaults to 'Loading...')
11255      */
11256     loadingText: 'Loading...',
11257     /**
11258      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11259      */
11260     resizable: false,
11261     /**
11262      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11263      */
11264     handleHeight : 8,
11265     /**
11266      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11267      * traditional select (defaults to true)
11268      */
11269     editable: true,
11270     /**
11271      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11272      */
11273     allQuery: '',
11274     /**
11275      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11276      */
11277     mode: 'remote',
11278     /**
11279      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11280      * listWidth has a higher value)
11281      */
11282     minListWidth : 70,
11283     /**
11284      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11285      * allow the user to set arbitrary text into the field (defaults to false)
11286      */
11287     forceSelection:false,
11288     /**
11289      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11290      * if typeAhead = true (defaults to 250)
11291      */
11292     typeAheadDelay : 250,
11293     /**
11294      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11295      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11296      */
11297     valueNotFoundText : undefined,
11298     /**
11299      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11300      */
11301     blockFocus : false,
11302     
11303     /**
11304      * @cfg {Boolean} disableClear Disable showing of clear button.
11305      */
11306     disableClear : false,
11307     /**
11308      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11309      */
11310     alwaysQuery : false,
11311     
11312     /**
11313      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11314      */
11315     multiple : false,
11316     
11317     /**
11318      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11319      */
11320     invalidClass : "has-warning",
11321     
11322     /**
11323      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11324      */
11325     validClass : "has-success",
11326     
11327     /**
11328      * @cfg {Boolean} specialFilter (true|false) special filter default false
11329      */
11330     specialFilter : false,
11331     
11332     /**
11333      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11334      */
11335     mobileTouchView : true,
11336     
11337     //private
11338     addicon : false,
11339     editicon: false,
11340     
11341     page: 0,
11342     hasQuery: false,
11343     append: false,
11344     loadNext: false,
11345     autoFocus : true,
11346     tickable : false,
11347     btnPosition : 'right',
11348     triggerList : true,
11349     showToggleBtn : true,
11350     animate : true,
11351     emptyResultText: 'Empty',
11352     // element that contains real text value.. (when hidden is used..)
11353     
11354     getAutoCreate : function()
11355     {
11356         var cfg = false;
11357         
11358         /*
11359          * Touch Devices
11360          */
11361         
11362         if(Roo.isTouch && this.mobileTouchView){
11363             cfg = this.getAutoCreateTouchView();
11364             return cfg;;
11365         }
11366         
11367         /*
11368          *  Normal ComboBox
11369          */
11370         if(!this.tickable){
11371             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11372             return cfg;
11373         }
11374         
11375         /*
11376          *  ComboBox with tickable selections
11377          */
11378              
11379         var align = this.labelAlign || this.parentLabelAlign();
11380         
11381         cfg = {
11382             cls : 'form-group roo-combobox-tickable' //input-group
11383         };
11384         
11385         var buttons = {
11386             tag : 'div',
11387             cls : 'tickable-buttons',
11388             cn : [
11389                 {
11390                     tag : 'button',
11391                     type : 'button',
11392                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11393                     html : 'Edit'
11394                 },
11395                 {
11396                     tag : 'button',
11397                     type : 'button',
11398                     name : 'ok',
11399                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11400                     html : 'Done'
11401                 },
11402                 {
11403                     tag : 'button',
11404                     type : 'button',
11405                     name : 'cancel',
11406                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11407                     html : 'Cancel'
11408                 }
11409             ]
11410         };
11411         
11412         if(this.editable){
11413             buttons.cn.unshift({
11414                 tag: 'input',
11415                 cls: 'select2-search-field-input'
11416             });
11417         }
11418         
11419         var _this = this;
11420         
11421         Roo.each(buttons.cn, function(c){
11422             if (_this.size) {
11423                 c.cls += ' btn-' + _this.size;
11424             }
11425
11426             if (_this.disabled) {
11427                 c.disabled = true;
11428             }
11429         });
11430         
11431         var box = {
11432             tag: 'div',
11433             cn: [
11434                 {
11435                     tag: 'input',
11436                     type : 'hidden',
11437                     cls: 'form-hidden-field'
11438                 },
11439                 {
11440                     tag: 'ul',
11441                     cls: 'select2-choices',
11442                     cn:[
11443                         {
11444                             tag: 'li',
11445                             cls: 'select2-search-field',
11446                             cn: [
11447
11448                                 buttons
11449                             ]
11450                         }
11451                     ]
11452                 }
11453             ]
11454         }
11455         
11456         var combobox = {
11457             cls: 'select2-container input-group select2-container-multi',
11458             cn: [
11459                 box
11460 //                {
11461 //                    tag: 'ul',
11462 //                    cls: 'typeahead typeahead-long dropdown-menu',
11463 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11464 //                }
11465             ]
11466         };
11467         
11468         if(this.hasFeedback && !this.allowBlank){
11469             
11470             var feedback = {
11471                 tag: 'span',
11472                 cls: 'glyphicon form-control-feedback'
11473             };
11474
11475             combobox.cn.push(feedback);
11476         }
11477         
11478         if (align ==='left' && this.fieldLabel.length) {
11479             
11480                 Roo.log("left and has label");
11481                 cfg.cn = [
11482                     
11483                     {
11484                         tag: 'label',
11485                         'for' :  id,
11486                         cls : 'control-label col-sm-' + this.labelWidth,
11487                         html : this.fieldLabel
11488                         
11489                     },
11490                     {
11491                         cls : "col-sm-" + (12 - this.labelWidth), 
11492                         cn: [
11493                             combobox
11494                         ]
11495                     }
11496                     
11497                 ];
11498         } else if ( this.fieldLabel.length) {
11499                 Roo.log(" label");
11500                  cfg.cn = [
11501                    
11502                     {
11503                         tag: 'label',
11504                         //cls : 'input-group-addon',
11505                         html : this.fieldLabel
11506                         
11507                     },
11508                     
11509                     combobox
11510                     
11511                 ];
11512
11513         } else {
11514             
11515                 Roo.log(" no label && no align");
11516                 cfg = combobox
11517                      
11518                 
11519         }
11520          
11521         var settings=this;
11522         ['xs','sm','md','lg'].map(function(size){
11523             if (settings[size]) {
11524                 cfg.cls += ' col-' + size + '-' + settings[size];
11525             }
11526         });
11527         
11528         return cfg;
11529         
11530     },
11531     
11532     // private
11533     initEvents: function()
11534     {
11535         
11536         if (!this.store) {
11537             throw "can not find store for combo";
11538         }
11539         
11540         this.store = Roo.factory(this.store, Roo.data);
11541         
11542         /*
11543          * Touch Devices
11544          */
11545         
11546         if(Roo.isTouch && this.mobileTouchView){
11547             this.initTouchView();
11548             return;
11549         }
11550         
11551         if(this.tickable){
11552             this.initTickableEvents();
11553             return;
11554         }
11555         
11556         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11557         
11558         if(this.hiddenName){
11559             
11560             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11561             
11562             this.hiddenField.dom.value =
11563                 this.hiddenValue !== undefined ? this.hiddenValue :
11564                 this.value !== undefined ? this.value : '';
11565
11566             // prevent input submission
11567             this.el.dom.removeAttribute('name');
11568             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11569              
11570              
11571         }
11572         //if(Roo.isGecko){
11573         //    this.el.dom.setAttribute('autocomplete', 'off');
11574         //}
11575         
11576         var cls = 'x-combo-list';
11577         
11578         //this.list = new Roo.Layer({
11579         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11580         //});
11581         
11582         var _this = this;
11583         
11584         (function(){
11585             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11586             _this.list.setWidth(lw);
11587         }).defer(100);
11588         
11589         this.list.on('mouseover', this.onViewOver, this);
11590         this.list.on('mousemove', this.onViewMove, this);
11591         
11592         this.list.on('scroll', this.onViewScroll, this);
11593         
11594         /*
11595         this.list.swallowEvent('mousewheel');
11596         this.assetHeight = 0;
11597
11598         if(this.title){
11599             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11600             this.assetHeight += this.header.getHeight();
11601         }
11602
11603         this.innerList = this.list.createChild({cls:cls+'-inner'});
11604         this.innerList.on('mouseover', this.onViewOver, this);
11605         this.innerList.on('mousemove', this.onViewMove, this);
11606         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11607         
11608         if(this.allowBlank && !this.pageSize && !this.disableClear){
11609             this.footer = this.list.createChild({cls:cls+'-ft'});
11610             this.pageTb = new Roo.Toolbar(this.footer);
11611            
11612         }
11613         if(this.pageSize){
11614             this.footer = this.list.createChild({cls:cls+'-ft'});
11615             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11616                     {pageSize: this.pageSize});
11617             
11618         }
11619         
11620         if (this.pageTb && this.allowBlank && !this.disableClear) {
11621             var _this = this;
11622             this.pageTb.add(new Roo.Toolbar.Fill(), {
11623                 cls: 'x-btn-icon x-btn-clear',
11624                 text: '&#160;',
11625                 handler: function()
11626                 {
11627                     _this.collapse();
11628                     _this.clearValue();
11629                     _this.onSelect(false, -1);
11630                 }
11631             });
11632         }
11633         if (this.footer) {
11634             this.assetHeight += this.footer.getHeight();
11635         }
11636         */
11637             
11638         if(!this.tpl){
11639             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11640         }
11641
11642         this.view = new Roo.View(this.list, this.tpl, {
11643             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11644         });
11645         //this.view.wrapEl.setDisplayed(false);
11646         this.view.on('click', this.onViewClick, this);
11647         
11648         
11649         
11650         this.store.on('beforeload', this.onBeforeLoad, this);
11651         this.store.on('load', this.onLoad, this);
11652         this.store.on('loadexception', this.onLoadException, this);
11653         /*
11654         if(this.resizable){
11655             this.resizer = new Roo.Resizable(this.list,  {
11656                pinned:true, handles:'se'
11657             });
11658             this.resizer.on('resize', function(r, w, h){
11659                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11660                 this.listWidth = w;
11661                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11662                 this.restrictHeight();
11663             }, this);
11664             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11665         }
11666         */
11667         if(!this.editable){
11668             this.editable = true;
11669             this.setEditable(false);
11670         }
11671         
11672         /*
11673         
11674         if (typeof(this.events.add.listeners) != 'undefined') {
11675             
11676             this.addicon = this.wrap.createChild(
11677                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11678        
11679             this.addicon.on('click', function(e) {
11680                 this.fireEvent('add', this);
11681             }, this);
11682         }
11683         if (typeof(this.events.edit.listeners) != 'undefined') {
11684             
11685             this.editicon = this.wrap.createChild(
11686                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11687             if (this.addicon) {
11688                 this.editicon.setStyle('margin-left', '40px');
11689             }
11690             this.editicon.on('click', function(e) {
11691                 
11692                 // we fire even  if inothing is selected..
11693                 this.fireEvent('edit', this, this.lastData );
11694                 
11695             }, this);
11696         }
11697         */
11698         
11699         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11700             "up" : function(e){
11701                 this.inKeyMode = true;
11702                 this.selectPrev();
11703             },
11704
11705             "down" : function(e){
11706                 if(!this.isExpanded()){
11707                     this.onTriggerClick();
11708                 }else{
11709                     this.inKeyMode = true;
11710                     this.selectNext();
11711                 }
11712             },
11713
11714             "enter" : function(e){
11715 //                this.onViewClick();
11716                 //return true;
11717                 this.collapse();
11718                 
11719                 if(this.fireEvent("specialkey", this, e)){
11720                     this.onViewClick(false);
11721                 }
11722                 
11723                 return true;
11724             },
11725
11726             "esc" : function(e){
11727                 this.collapse();
11728             },
11729
11730             "tab" : function(e){
11731                 this.collapse();
11732                 
11733                 if(this.fireEvent("specialkey", this, e)){
11734                     this.onViewClick(false);
11735                 }
11736                 
11737                 return true;
11738             },
11739
11740             scope : this,
11741
11742             doRelay : function(foo, bar, hname){
11743                 if(hname == 'down' || this.scope.isExpanded()){
11744                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11745                 }
11746                 return true;
11747             },
11748
11749             forceKeyDown: true
11750         });
11751         
11752         
11753         this.queryDelay = Math.max(this.queryDelay || 10,
11754                 this.mode == 'local' ? 10 : 250);
11755         
11756         
11757         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11758         
11759         if(this.typeAhead){
11760             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11761         }
11762         if(this.editable !== false){
11763             this.inputEl().on("keyup", this.onKeyUp, this);
11764         }
11765         if(this.forceSelection){
11766             this.inputEl().on('blur', this.doForce, this);
11767         }
11768         
11769         if(this.multiple){
11770             this.choices = this.el.select('ul.select2-choices', true).first();
11771             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11772         }
11773     },
11774     
11775     initTickableEvents: function()
11776     {   
11777         this.createList();
11778         
11779         if(this.hiddenName){
11780             
11781             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11782             
11783             this.hiddenField.dom.value =
11784                 this.hiddenValue !== undefined ? this.hiddenValue :
11785                 this.value !== undefined ? this.value : '';
11786
11787             // prevent input submission
11788             this.el.dom.removeAttribute('name');
11789             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11790              
11791              
11792         }
11793         
11794 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11795         
11796         this.choices = this.el.select('ul.select2-choices', true).first();
11797         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11798         if(this.triggerList){
11799             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11800         }
11801          
11802         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11803         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11804         
11805         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11806         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11807         
11808         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11809         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11810         
11811         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11812         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11813         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11814         
11815         this.okBtn.hide();
11816         this.cancelBtn.hide();
11817         
11818         var _this = this;
11819         
11820         (function(){
11821             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11822             _this.list.setWidth(lw);
11823         }).defer(100);
11824         
11825         this.list.on('mouseover', this.onViewOver, this);
11826         this.list.on('mousemove', this.onViewMove, this);
11827         
11828         this.list.on('scroll', this.onViewScroll, this);
11829         
11830         if(!this.tpl){
11831             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>';
11832         }
11833
11834         this.view = new Roo.View(this.list, this.tpl, {
11835             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11836         });
11837         
11838         //this.view.wrapEl.setDisplayed(false);
11839         this.view.on('click', this.onViewClick, this);
11840         
11841         
11842         
11843         this.store.on('beforeload', this.onBeforeLoad, this);
11844         this.store.on('load', this.onLoad, this);
11845         this.store.on('loadexception', this.onLoadException, this);
11846         
11847         if(this.editable){
11848             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11849                 "up" : function(e){
11850                     this.inKeyMode = true;
11851                     this.selectPrev();
11852                 },
11853
11854                 "down" : function(e){
11855                     this.inKeyMode = true;
11856                     this.selectNext();
11857                 },
11858
11859                 "enter" : function(e){
11860                     if(this.fireEvent("specialkey", this, e)){
11861                         this.onViewClick(false);
11862                     }
11863                     
11864                     return true;
11865                 },
11866
11867                 "esc" : function(e){
11868                     this.onTickableFooterButtonClick(e, false, false);
11869                 },
11870
11871                 "tab" : function(e){
11872                     this.fireEvent("specialkey", this, e);
11873                     
11874                     this.onTickableFooterButtonClick(e, false, false);
11875                     
11876                     return true;
11877                 },
11878
11879                 scope : this,
11880
11881                 doRelay : function(e, fn, key){
11882                     if(this.scope.isExpanded()){
11883                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11884                     }
11885                     return true;
11886                 },
11887
11888                 forceKeyDown: true
11889             });
11890         }
11891         
11892         this.queryDelay = Math.max(this.queryDelay || 10,
11893                 this.mode == 'local' ? 10 : 250);
11894         
11895         
11896         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11897         
11898         if(this.typeAhead){
11899             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11900         }
11901         
11902         if(this.editable !== false){
11903             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11904         }
11905         
11906     },
11907
11908     onDestroy : function(){
11909         if(this.view){
11910             this.view.setStore(null);
11911             this.view.el.removeAllListeners();
11912             this.view.el.remove();
11913             this.view.purgeListeners();
11914         }
11915         if(this.list){
11916             this.list.dom.innerHTML  = '';
11917         }
11918         
11919         if(this.store){
11920             this.store.un('beforeload', this.onBeforeLoad, this);
11921             this.store.un('load', this.onLoad, this);
11922             this.store.un('loadexception', this.onLoadException, this);
11923         }
11924         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11925     },
11926
11927     // private
11928     fireKey : function(e){
11929         if(e.isNavKeyPress() && !this.list.isVisible()){
11930             this.fireEvent("specialkey", this, e);
11931         }
11932     },
11933
11934     // private
11935     onResize: function(w, h){
11936 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11937 //        
11938 //        if(typeof w != 'number'){
11939 //            // we do not handle it!?!?
11940 //            return;
11941 //        }
11942 //        var tw = this.trigger.getWidth();
11943 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11944 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11945 //        var x = w - tw;
11946 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11947 //            
11948 //        //this.trigger.setStyle('left', x+'px');
11949 //        
11950 //        if(this.list && this.listWidth === undefined){
11951 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11952 //            this.list.setWidth(lw);
11953 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11954 //        }
11955         
11956     
11957         
11958     },
11959
11960     /**
11961      * Allow or prevent the user from directly editing the field text.  If false is passed,
11962      * the user will only be able to select from the items defined in the dropdown list.  This method
11963      * is the runtime equivalent of setting the 'editable' config option at config time.
11964      * @param {Boolean} value True to allow the user to directly edit the field text
11965      */
11966     setEditable : function(value){
11967         if(value == this.editable){
11968             return;
11969         }
11970         this.editable = value;
11971         if(!value){
11972             this.inputEl().dom.setAttribute('readOnly', true);
11973             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11974             this.inputEl().addClass('x-combo-noedit');
11975         }else{
11976             this.inputEl().dom.setAttribute('readOnly', false);
11977             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11978             this.inputEl().removeClass('x-combo-noedit');
11979         }
11980     },
11981
11982     // private
11983     
11984     onBeforeLoad : function(combo,opts){
11985         if(!this.hasFocus){
11986             return;
11987         }
11988          if (!opts.add) {
11989             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11990          }
11991         this.restrictHeight();
11992         this.selectedIndex = -1;
11993     },
11994
11995     // private
11996     onLoad : function(){
11997         
11998         this.hasQuery = false;
11999         
12000         if(!this.hasFocus){
12001             return;
12002         }
12003         
12004         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12005             this.loading.hide();
12006         }
12007              
12008         if(this.store.getCount() > 0){
12009             this.expand();
12010             this.restrictHeight();
12011             if(this.lastQuery == this.allQuery){
12012                 if(this.editable && !this.tickable){
12013                     this.inputEl().dom.select();
12014                 }
12015                 
12016                 if(
12017                     !this.selectByValue(this.value, true) &&
12018                     this.autoFocus && 
12019                     (
12020                         !this.store.lastOptions ||
12021                         typeof(this.store.lastOptions.add) == 'undefined' || 
12022                         this.store.lastOptions.add != true
12023                     )
12024                 ){
12025                     this.select(0, true);
12026                 }
12027             }else{
12028                 if(this.autoFocus){
12029                     this.selectNext();
12030                 }
12031                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12032                     this.taTask.delay(this.typeAheadDelay);
12033                 }
12034             }
12035         }else{
12036             this.onEmptyResults();
12037         }
12038         
12039         //this.el.focus();
12040     },
12041     // private
12042     onLoadException : function()
12043     {
12044         this.hasQuery = false;
12045         
12046         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12047             this.loading.hide();
12048         }
12049         
12050         if(this.tickable && this.editable){
12051             return;
12052         }
12053         
12054         this.collapse();
12055         
12056         Roo.log(this.store.reader.jsonData);
12057         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12058             // fixme
12059             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12060         }
12061         
12062         
12063     },
12064     // private
12065     onTypeAhead : function(){
12066         if(this.store.getCount() > 0){
12067             var r = this.store.getAt(0);
12068             var newValue = r.data[this.displayField];
12069             var len = newValue.length;
12070             var selStart = this.getRawValue().length;
12071             
12072             if(selStart != len){
12073                 this.setRawValue(newValue);
12074                 this.selectText(selStart, newValue.length);
12075             }
12076         }
12077     },
12078
12079     // private
12080     onSelect : function(record, index){
12081         
12082         if(this.fireEvent('beforeselect', this, record, index) !== false){
12083         
12084             this.setFromData(index > -1 ? record.data : false);
12085             
12086             this.collapse();
12087             this.fireEvent('select', this, record, index);
12088         }
12089     },
12090
12091     /**
12092      * Returns the currently selected field value or empty string if no value is set.
12093      * @return {String} value The selected value
12094      */
12095     getValue : function(){
12096         
12097         if(this.multiple){
12098             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12099         }
12100         
12101         if(this.valueField){
12102             return typeof this.value != 'undefined' ? this.value : '';
12103         }else{
12104             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12105         }
12106     },
12107
12108     /**
12109      * Clears any text/value currently set in the field
12110      */
12111     clearValue : function(){
12112         if(this.hiddenField){
12113             this.hiddenField.dom.value = '';
12114         }
12115         this.value = '';
12116         this.setRawValue('');
12117         this.lastSelectionText = '';
12118         this.lastData = false;
12119         
12120         var close = this.closeTriggerEl();
12121         
12122         if(close){
12123             close.hide();
12124         }
12125         
12126     },
12127
12128     /**
12129      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12130      * will be displayed in the field.  If the value does not match the data value of an existing item,
12131      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12132      * Otherwise the field will be blank (although the value will still be set).
12133      * @param {String} value The value to match
12134      */
12135     setValue : function(v){
12136         if(this.multiple){
12137             this.syncValue();
12138             return;
12139         }
12140         
12141         var text = v;
12142         if(this.valueField){
12143             var r = this.findRecord(this.valueField, v);
12144             if(r){
12145                 text = r.data[this.displayField];
12146             }else if(this.valueNotFoundText !== undefined){
12147                 text = this.valueNotFoundText;
12148             }
12149         }
12150         this.lastSelectionText = text;
12151         if(this.hiddenField){
12152             this.hiddenField.dom.value = v;
12153         }
12154         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12155         this.value = v;
12156         
12157         var close = this.closeTriggerEl();
12158         
12159         if(close){
12160             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12161         }
12162     },
12163     /**
12164      * @property {Object} the last set data for the element
12165      */
12166     
12167     lastData : false,
12168     /**
12169      * Sets the value of the field based on a object which is related to the record format for the store.
12170      * @param {Object} value the value to set as. or false on reset?
12171      */
12172     setFromData : function(o){
12173         
12174         if(this.multiple){
12175             this.addItem(o);
12176             return;
12177         }
12178             
12179         var dv = ''; // display value
12180         var vv = ''; // value value..
12181         this.lastData = o;
12182         if (this.displayField) {
12183             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12184         } else {
12185             // this is an error condition!!!
12186             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12187         }
12188         
12189         if(this.valueField){
12190             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12191         }
12192         
12193         var close = this.closeTriggerEl();
12194         
12195         if(close){
12196             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12197         }
12198         
12199         if(this.hiddenField){
12200             this.hiddenField.dom.value = vv;
12201             
12202             this.lastSelectionText = dv;
12203             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12204             this.value = vv;
12205             return;
12206         }
12207         // no hidden field.. - we store the value in 'value', but still display
12208         // display field!!!!
12209         this.lastSelectionText = dv;
12210         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12211         this.value = vv;
12212         
12213         
12214         
12215     },
12216     // private
12217     reset : function(){
12218         // overridden so that last data is reset..
12219         
12220         if(this.multiple){
12221             this.clearItem();
12222             return;
12223         }
12224         
12225         this.setValue(this.originalValue);
12226         this.clearInvalid();
12227         this.lastData = false;
12228         if (this.view) {
12229             this.view.clearSelections();
12230         }
12231     },
12232     // private
12233     findRecord : function(prop, value){
12234         var record;
12235         if(this.store.getCount() > 0){
12236             this.store.each(function(r){
12237                 if(r.data[prop] == value){
12238                     record = r;
12239                     return false;
12240                 }
12241                 return true;
12242             });
12243         }
12244         return record;
12245     },
12246     
12247     getName: function()
12248     {
12249         // returns hidden if it's set..
12250         if (!this.rendered) {return ''};
12251         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12252         
12253     },
12254     // private
12255     onViewMove : function(e, t){
12256         this.inKeyMode = false;
12257     },
12258
12259     // private
12260     onViewOver : function(e, t){
12261         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12262             return;
12263         }
12264         var item = this.view.findItemFromChild(t);
12265         
12266         if(item){
12267             var index = this.view.indexOf(item);
12268             this.select(index, false);
12269         }
12270     },
12271
12272     // private
12273     onViewClick : function(view, doFocus, el, e)
12274     {
12275         var index = this.view.getSelectedIndexes()[0];
12276         
12277         var r = this.store.getAt(index);
12278         
12279         if(this.tickable){
12280             
12281             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12282                 return;
12283             }
12284             
12285             var rm = false;
12286             var _this = this;
12287             
12288             Roo.each(this.tickItems, function(v,k){
12289                 
12290                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12291                     _this.tickItems.splice(k, 1);
12292                     
12293                     if(typeof(e) == 'undefined' && view == false){
12294                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12295                     }
12296                     
12297                     rm = true;
12298                     return;
12299                 }
12300             });
12301             
12302             if(rm){
12303                 return;
12304             }
12305             
12306             this.tickItems.push(r.data);
12307             
12308             if(typeof(e) == 'undefined' && view == false){
12309                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12310             }
12311                     
12312             return;
12313         }
12314         
12315         if(r){
12316             this.onSelect(r, index);
12317         }
12318         if(doFocus !== false && !this.blockFocus){
12319             this.inputEl().focus();
12320         }
12321     },
12322
12323     // private
12324     restrictHeight : function(){
12325         //this.innerList.dom.style.height = '';
12326         //var inner = this.innerList.dom;
12327         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12328         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12329         //this.list.beginUpdate();
12330         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12331         this.list.alignTo(this.inputEl(), this.listAlign);
12332         this.list.alignTo(this.inputEl(), this.listAlign);
12333         //this.list.endUpdate();
12334     },
12335
12336     // private
12337     onEmptyResults : function(){
12338         
12339         if(this.tickable && this.editable){
12340             this.restrictHeight();
12341             return;
12342         }
12343         
12344         this.collapse();
12345     },
12346
12347     /**
12348      * Returns true if the dropdown list is expanded, else false.
12349      */
12350     isExpanded : function(){
12351         return this.list.isVisible();
12352     },
12353
12354     /**
12355      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12356      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12357      * @param {String} value The data value of the item to select
12358      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12359      * selected item if it is not currently in view (defaults to true)
12360      * @return {Boolean} True if the value matched an item in the list, else false
12361      */
12362     selectByValue : function(v, scrollIntoView){
12363         if(v !== undefined && v !== null){
12364             var r = this.findRecord(this.valueField || this.displayField, v);
12365             if(r){
12366                 this.select(this.store.indexOf(r), scrollIntoView);
12367                 return true;
12368             }
12369         }
12370         return false;
12371     },
12372
12373     /**
12374      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12375      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12376      * @param {Number} index The zero-based index of the list item to select
12377      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12378      * selected item if it is not currently in view (defaults to true)
12379      */
12380     select : function(index, scrollIntoView){
12381         this.selectedIndex = index;
12382         this.view.select(index);
12383         if(scrollIntoView !== false){
12384             var el = this.view.getNode(index);
12385             /*
12386              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12387              */
12388             if(el){
12389                 this.list.scrollChildIntoView(el, false);
12390             }
12391         }
12392     },
12393
12394     // private
12395     selectNext : function(){
12396         var ct = this.store.getCount();
12397         if(ct > 0){
12398             if(this.selectedIndex == -1){
12399                 this.select(0);
12400             }else if(this.selectedIndex < ct-1){
12401                 this.select(this.selectedIndex+1);
12402             }
12403         }
12404     },
12405
12406     // private
12407     selectPrev : function(){
12408         var ct = this.store.getCount();
12409         if(ct > 0){
12410             if(this.selectedIndex == -1){
12411                 this.select(0);
12412             }else if(this.selectedIndex != 0){
12413                 this.select(this.selectedIndex-1);
12414             }
12415         }
12416     },
12417
12418     // private
12419     onKeyUp : function(e){
12420         if(this.editable !== false && !e.isSpecialKey()){
12421             this.lastKey = e.getKey();
12422             this.dqTask.delay(this.queryDelay);
12423         }
12424     },
12425
12426     // private
12427     validateBlur : function(){
12428         return !this.list || !this.list.isVisible();   
12429     },
12430
12431     // private
12432     initQuery : function(){
12433         
12434         var v = this.getRawValue();
12435         
12436         if(this.tickable && this.editable){
12437             v = this.tickableInputEl().getValue();
12438         }
12439         
12440         this.doQuery(v);
12441     },
12442
12443     // private
12444     doForce : function(){
12445         if(this.inputEl().dom.value.length > 0){
12446             this.inputEl().dom.value =
12447                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12448              
12449         }
12450     },
12451
12452     /**
12453      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12454      * query allowing the query action to be canceled if needed.
12455      * @param {String} query The SQL query to execute
12456      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12457      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12458      * saved in the current store (defaults to false)
12459      */
12460     doQuery : function(q, forceAll){
12461         
12462         if(q === undefined || q === null){
12463             q = '';
12464         }
12465         var qe = {
12466             query: q,
12467             forceAll: forceAll,
12468             combo: this,
12469             cancel:false
12470         };
12471         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12472             return false;
12473         }
12474         q = qe.query;
12475         
12476         forceAll = qe.forceAll;
12477         if(forceAll === true || (q.length >= this.minChars)){
12478             
12479             this.hasQuery = true;
12480             
12481             if(this.lastQuery != q || this.alwaysQuery){
12482                 this.lastQuery = q;
12483                 if(this.mode == 'local'){
12484                     this.selectedIndex = -1;
12485                     if(forceAll){
12486                         this.store.clearFilter();
12487                     }else{
12488                         
12489                         if(this.specialFilter){
12490                             this.fireEvent('specialfilter', this);
12491                             this.onLoad();
12492                             return;
12493                         }
12494                         
12495                         this.store.filter(this.displayField, q);
12496                     }
12497                     
12498                     this.store.fireEvent("datachanged", this.store);
12499                     
12500                     this.onLoad();
12501                     
12502                     
12503                 }else{
12504                     
12505                     this.store.baseParams[this.queryParam] = q;
12506                     
12507                     var options = {params : this.getParams(q)};
12508                     
12509                     if(this.loadNext){
12510                         options.add = true;
12511                         options.params.start = this.page * this.pageSize;
12512                     }
12513                     
12514                     this.store.load(options);
12515                     
12516                     /*
12517                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12518                      *  we should expand the list on onLoad
12519                      *  so command out it
12520                      */
12521 //                    this.expand();
12522                 }
12523             }else{
12524                 this.selectedIndex = -1;
12525                 this.onLoad();   
12526             }
12527         }
12528         
12529         this.loadNext = false;
12530     },
12531     
12532     // private
12533     getParams : function(q){
12534         var p = {};
12535         //p[this.queryParam] = q;
12536         
12537         if(this.pageSize){
12538             p.start = 0;
12539             p.limit = this.pageSize;
12540         }
12541         return p;
12542     },
12543
12544     /**
12545      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12546      */
12547     collapse : function(){
12548         if(!this.isExpanded()){
12549             return;
12550         }
12551         
12552         this.list.hide();
12553         
12554         if(this.tickable){
12555             this.hasFocus = false;
12556             this.okBtn.hide();
12557             this.cancelBtn.hide();
12558             this.trigger.show();
12559             
12560             if(this.editable){
12561                 this.tickableInputEl().dom.value = '';
12562                 this.tickableInputEl().blur();
12563             }
12564             
12565         }
12566         
12567         Roo.get(document).un('mousedown', this.collapseIf, this);
12568         Roo.get(document).un('mousewheel', this.collapseIf, this);
12569         if (!this.editable) {
12570             Roo.get(document).un('keydown', this.listKeyPress, this);
12571         }
12572         this.fireEvent('collapse', this);
12573     },
12574
12575     // private
12576     collapseIf : function(e){
12577         var in_combo  = e.within(this.el);
12578         var in_list =  e.within(this.list);
12579         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12580         
12581         if (in_combo || in_list || is_list) {
12582             //e.stopPropagation();
12583             return;
12584         }
12585         
12586         if(this.tickable){
12587             this.onTickableFooterButtonClick(e, false, false);
12588         }
12589
12590         this.collapse();
12591         
12592     },
12593
12594     /**
12595      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12596      */
12597     expand : function(){
12598        
12599         if(this.isExpanded() || !this.hasFocus){
12600             return;
12601         }
12602         
12603         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12604         this.list.setWidth(lw);
12605         
12606         
12607          Roo.log('expand');
12608         
12609         this.list.show();
12610         
12611         this.restrictHeight();
12612         
12613         if(this.tickable){
12614             
12615             this.tickItems = Roo.apply([], this.item);
12616             
12617             this.okBtn.show();
12618             this.cancelBtn.show();
12619             this.trigger.hide();
12620             
12621             if(this.editable){
12622                 this.tickableInputEl().focus();
12623             }
12624             
12625         }
12626         
12627         Roo.get(document).on('mousedown', this.collapseIf, this);
12628         Roo.get(document).on('mousewheel', this.collapseIf, this);
12629         if (!this.editable) {
12630             Roo.get(document).on('keydown', this.listKeyPress, this);
12631         }
12632         
12633         this.fireEvent('expand', this);
12634     },
12635
12636     // private
12637     // Implements the default empty TriggerField.onTriggerClick function
12638     onTriggerClick : function(e)
12639     {
12640         Roo.log('trigger click');
12641         
12642         if(this.disabled || !this.triggerList){
12643             return;
12644         }
12645         
12646         this.page = 0;
12647         this.loadNext = false;
12648         
12649         if(this.isExpanded()){
12650             this.collapse();
12651             if (!this.blockFocus) {
12652                 this.inputEl().focus();
12653             }
12654             
12655         }else {
12656             this.hasFocus = true;
12657             if(this.triggerAction == 'all') {
12658                 this.doQuery(this.allQuery, true);
12659             } else {
12660                 this.doQuery(this.getRawValue());
12661             }
12662             if (!this.blockFocus) {
12663                 this.inputEl().focus();
12664             }
12665         }
12666     },
12667     
12668     onTickableTriggerClick : function(e)
12669     {
12670         if(this.disabled){
12671             return;
12672         }
12673         
12674         this.page = 0;
12675         this.loadNext = false;
12676         this.hasFocus = true;
12677         
12678         if(this.triggerAction == 'all') {
12679             this.doQuery(this.allQuery, true);
12680         } else {
12681             this.doQuery(this.getRawValue());
12682         }
12683     },
12684     
12685     onSearchFieldClick : function(e)
12686     {
12687         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12688             this.onTickableFooterButtonClick(e, false, false);
12689             return;
12690         }
12691         
12692         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12693             return;
12694         }
12695         
12696         this.page = 0;
12697         this.loadNext = false;
12698         this.hasFocus = true;
12699         
12700         if(this.triggerAction == 'all') {
12701             this.doQuery(this.allQuery, true);
12702         } else {
12703             this.doQuery(this.getRawValue());
12704         }
12705     },
12706     
12707     listKeyPress : function(e)
12708     {
12709         //Roo.log('listkeypress');
12710         // scroll to first matching element based on key pres..
12711         if (e.isSpecialKey()) {
12712             return false;
12713         }
12714         var k = String.fromCharCode(e.getKey()).toUpperCase();
12715         //Roo.log(k);
12716         var match  = false;
12717         var csel = this.view.getSelectedNodes();
12718         var cselitem = false;
12719         if (csel.length) {
12720             var ix = this.view.indexOf(csel[0]);
12721             cselitem  = this.store.getAt(ix);
12722             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12723                 cselitem = false;
12724             }
12725             
12726         }
12727         
12728         this.store.each(function(v) { 
12729             if (cselitem) {
12730                 // start at existing selection.
12731                 if (cselitem.id == v.id) {
12732                     cselitem = false;
12733                 }
12734                 return true;
12735             }
12736                 
12737             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12738                 match = this.store.indexOf(v);
12739                 return false;
12740             }
12741             return true;
12742         }, this);
12743         
12744         if (match === false) {
12745             return true; // no more action?
12746         }
12747         // scroll to?
12748         this.view.select(match);
12749         var sn = Roo.get(this.view.getSelectedNodes()[0])
12750         sn.scrollIntoView(sn.dom.parentNode, false);
12751     },
12752     
12753     onViewScroll : function(e, t){
12754         
12755         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){
12756             return;
12757         }
12758         
12759         this.hasQuery = true;
12760         
12761         this.loading = this.list.select('.loading', true).first();
12762         
12763         if(this.loading === null){
12764             this.list.createChild({
12765                 tag: 'div',
12766                 cls: 'loading select2-more-results select2-active',
12767                 html: 'Loading more results...'
12768             })
12769             
12770             this.loading = this.list.select('.loading', true).first();
12771             
12772             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12773             
12774             this.loading.hide();
12775         }
12776         
12777         this.loading.show();
12778         
12779         var _combo = this;
12780         
12781         this.page++;
12782         this.loadNext = true;
12783         
12784         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12785         
12786         return;
12787     },
12788     
12789     addItem : function(o)
12790     {   
12791         var dv = ''; // display value
12792         
12793         if (this.displayField) {
12794             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12795         } else {
12796             // this is an error condition!!!
12797             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12798         }
12799         
12800         if(!dv.length){
12801             return;
12802         }
12803         
12804         var choice = this.choices.createChild({
12805             tag: 'li',
12806             cls: 'select2-search-choice',
12807             cn: [
12808                 {
12809                     tag: 'div',
12810                     html: dv
12811                 },
12812                 {
12813                     tag: 'a',
12814                     href: '#',
12815                     cls: 'select2-search-choice-close',
12816                     tabindex: '-1'
12817                 }
12818             ]
12819             
12820         }, this.searchField);
12821         
12822         var close = choice.select('a.select2-search-choice-close', true).first()
12823         
12824         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12825         
12826         this.item.push(o);
12827         
12828         this.lastData = o;
12829         
12830         this.syncValue();
12831         
12832         this.inputEl().dom.value = '';
12833         
12834         this.validate();
12835     },
12836     
12837     onRemoveItem : function(e, _self, o)
12838     {
12839         e.preventDefault();
12840         
12841         this.lastItem = Roo.apply([], this.item);
12842         
12843         var index = this.item.indexOf(o.data) * 1;
12844         
12845         if( index < 0){
12846             Roo.log('not this item?!');
12847             return;
12848         }
12849         
12850         this.item.splice(index, 1);
12851         o.item.remove();
12852         
12853         this.syncValue();
12854         
12855         this.fireEvent('remove', this, e);
12856         
12857         this.validate();
12858         
12859     },
12860     
12861     syncValue : function()
12862     {
12863         if(!this.item.length){
12864             this.clearValue();
12865             return;
12866         }
12867             
12868         var value = [];
12869         var _this = this;
12870         Roo.each(this.item, function(i){
12871             if(_this.valueField){
12872                 value.push(i[_this.valueField]);
12873                 return;
12874             }
12875
12876             value.push(i);
12877         });
12878
12879         this.value = value.join(',');
12880
12881         if(this.hiddenField){
12882             this.hiddenField.dom.value = this.value;
12883         }
12884         
12885         this.store.fireEvent("datachanged", this.store);
12886     },
12887     
12888     clearItem : function()
12889     {
12890         if(!this.multiple){
12891             return;
12892         }
12893         
12894         this.item = [];
12895         
12896         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12897            c.remove();
12898         });
12899         
12900         this.syncValue();
12901         
12902         this.validate();
12903     },
12904     
12905     inputEl: function ()
12906     {
12907         if(Roo.isTouch && this.mobileTouchView){
12908             return this.el.select('input.form-control',true).first();
12909         }
12910         
12911         if(this.tickable){
12912             return this.searchField;
12913         }
12914         
12915         return this.el.select('input.form-control',true).first();
12916     },
12917     
12918     
12919     onTickableFooterButtonClick : function(e, btn, el)
12920     {
12921         e.preventDefault();
12922         
12923         this.lastItem = Roo.apply([], this.item);
12924         
12925         if(btn && btn.name == 'cancel'){
12926             this.tickItems = Roo.apply([], this.item);
12927             this.collapse();
12928             return;
12929         }
12930         
12931         this.clearItem();
12932         
12933         var _this = this;
12934         
12935         Roo.each(this.tickItems, function(o){
12936             _this.addItem(o);
12937         });
12938         
12939         this.collapse();
12940         
12941     },
12942     
12943     validate : function()
12944     {
12945         var v = this.getRawValue();
12946         
12947         if(this.multiple){
12948             v = this.getValue();
12949         }
12950         
12951         if(this.disabled || this.allowBlank || v.length){
12952             this.markValid();
12953             return true;
12954         }
12955         
12956         this.markInvalid();
12957         return false;
12958     },
12959     
12960     tickableInputEl : function()
12961     {
12962         if(!this.tickable || !this.editable){
12963             return this.inputEl();
12964         }
12965         
12966         return this.inputEl().select('.select2-search-field-input', true).first();
12967     },
12968     
12969     
12970     getAutoCreateTouchView : function()
12971     {
12972         var id = Roo.id();
12973         
12974         var cfg = {
12975             cls: 'form-group' //input-group
12976         };
12977         
12978         var input =  {
12979             tag: 'input',
12980             id : id,
12981             type : this.inputType,
12982             cls : 'form-control x-combo-noedit',
12983             autocomplete: 'new-password',
12984             placeholder : this.placeholder || '',
12985             readonly : true
12986         };
12987         
12988         if (this.name) {
12989             input.name = this.name;
12990         }
12991         
12992         if (this.size) {
12993             input.cls += ' input-' + this.size;
12994         }
12995         
12996         if (this.disabled) {
12997             input.disabled = true;
12998         }
12999         
13000         var inputblock = {
13001             cls : '',
13002             cn : [
13003                 input
13004             ]
13005         };
13006         
13007         if(this.before){
13008             inputblock.cls += ' input-group';
13009             
13010             inputblock.cn.unshift({
13011                 tag :'span',
13012                 cls : 'input-group-addon',
13013                 html : this.before
13014             });
13015         }
13016         
13017         if(this.removable && !this.multiple){
13018             inputblock.cls += ' roo-removable';
13019             
13020             inputblock.cn.push({
13021                 tag: 'button',
13022                 html : 'x',
13023                 cls : 'roo-combo-removable-btn close'
13024             });
13025         }
13026
13027         if(this.hasFeedback && !this.allowBlank){
13028             
13029             inputblock.cls += ' has-feedback';
13030             
13031             inputblock.cn.push({
13032                 tag: 'span',
13033                 cls: 'glyphicon form-control-feedback'
13034             });
13035             
13036         }
13037         
13038         if (this.after) {
13039             
13040             inputblock.cls += (this.before) ? '' : ' input-group';
13041             
13042             inputblock.cn.push({
13043                 tag :'span',
13044                 cls : 'input-group-addon',
13045                 html : this.after
13046             });
13047         }
13048
13049         var box = {
13050             tag: 'div',
13051             cn: [
13052                 {
13053                     tag: 'input',
13054                     type : 'hidden',
13055                     cls: 'form-hidden-field'
13056                 },
13057                 inputblock
13058             ]
13059             
13060         };
13061         
13062         if(this.multiple){
13063             box = {
13064                 tag: 'div',
13065                 cn: [
13066                     {
13067                         tag: 'input',
13068                         type : 'hidden',
13069                         cls: 'form-hidden-field'
13070                     },
13071                     {
13072                         tag: 'ul',
13073                         cls: 'select2-choices',
13074                         cn:[
13075                             {
13076                                 tag: 'li',
13077                                 cls: 'select2-search-field',
13078                                 cn: [
13079
13080                                     inputblock
13081                                 ]
13082                             }
13083                         ]
13084                     }
13085                 ]
13086             }
13087         };
13088         
13089         var combobox = {
13090             cls: 'select2-container input-group',
13091             cn: [
13092                 box
13093             ]
13094         };
13095         
13096         if(this.multiple){
13097             combobox.cls += ' select2-container-multi';
13098         }
13099         
13100         var align = this.labelAlign || this.parentLabelAlign();
13101         
13102         cfg.cn = combobox;
13103         
13104         if(this.fieldLabel.length){
13105             
13106             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13107             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13108             
13109             cfg.cn = [
13110                 {
13111                     tag: 'label',
13112                     cls : 'control-label ' + lw,
13113                     html : this.fieldLabel
13114
13115                 },
13116                 {
13117                     cls : cw, 
13118                     cn: [
13119                         combobox
13120                     ]
13121                 }
13122             ];
13123         }
13124         
13125         var settings = this;
13126         
13127         ['xs','sm','md','lg'].map(function(size){
13128             if (settings[size]) {
13129                 cfg.cls += ' col-' + size + '-' + settings[size];
13130             }
13131         });
13132         
13133         return cfg;
13134     },
13135     
13136     initTouchView : function()
13137     {
13138         this.renderTouchView();
13139         
13140         this.touchViewEl.on('scroll', function(){
13141             this.el.dom.scrollTop = 0;
13142         }, this);
13143         
13144         this.inputEl().on("click", this.showTouchView, this);
13145         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13146         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13147         
13148         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13149         
13150         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13151         this.store.on('load', this.onTouchViewLoad, this);
13152         this.store.on('loadexception', this.onTouchViewLoadException, this);
13153         
13154         if(this.hiddenName){
13155             
13156             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13157             
13158             this.hiddenField.dom.value =
13159                 this.hiddenValue !== undefined ? this.hiddenValue :
13160                 this.value !== undefined ? this.value : '';
13161         
13162             this.el.dom.removeAttribute('name');
13163             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13164         }
13165         
13166         if(this.multiple){
13167             this.choices = this.el.select('ul.select2-choices', true).first();
13168             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13169         }
13170         
13171         if(this.removable && !this.multiple){
13172             var close = this.closeTriggerEl();
13173             if(close){
13174                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13175                 close.on('click', this.removeBtnClick, this, close);
13176             }
13177         }
13178         
13179         return;
13180         
13181         
13182     },
13183     
13184     renderTouchView : function()
13185     {
13186         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13187         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13188         
13189         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13190         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13191         
13192         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13193         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13194         this.touchViewBodyEl.setStyle('overflow', 'auto');
13195         
13196         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13197         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13198         
13199         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13200         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13201         
13202     },
13203     
13204     showTouchView : function()
13205     {
13206         this.touchViewHeaderEl.hide();
13207
13208         if(this.fieldLabel.length){
13209             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13210             this.touchViewHeaderEl.show();
13211         }
13212
13213         this.touchViewEl.show();
13214
13215         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13216         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13217
13218         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13219
13220         if(this.fieldLabel.length){
13221             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13222         }
13223         
13224         this.touchViewBodyEl.setHeight(bodyHeight);
13225
13226         if(this.animate){
13227             var _this = this;
13228             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13229         }else{
13230             this.touchViewEl.addClass('in');
13231         }
13232
13233         this.doTouchViewQuery();
13234         
13235     },
13236     
13237     hideTouchView : function()
13238     {
13239         this.touchViewEl.removeClass('in');
13240
13241         if(this.animate){
13242             var _this = this;
13243             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13244         }else{
13245             this.touchViewEl.setStyle('display', 'none');
13246         }
13247         
13248     },
13249     
13250     setTouchViewValue : function()
13251     {
13252         if(this.multiple){
13253             this.clearItem();
13254         
13255             var _this = this;
13256
13257             Roo.each(this.tickItems, function(o){
13258                 this.addItem(o);
13259             }, this);
13260         }
13261         
13262         this.hideTouchView();
13263     },
13264     
13265     doTouchViewQuery : function()
13266     {
13267         var qe = {
13268             query: '',
13269             forceAll: true,
13270             combo: this,
13271             cancel:false
13272         };
13273         
13274         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13275             return false;
13276         }
13277         
13278         if(!this.alwaysQuery || this.mode == 'local'){
13279             this.onTouchViewLoad();
13280             return;
13281         }
13282         
13283         this.store.load();
13284     },
13285     
13286     onTouchViewBeforeLoad : function(combo,opts)
13287     {
13288         return;
13289     },
13290
13291     // private
13292     onTouchViewLoad : function()
13293     {
13294         if(this.store.getCount() < 1){
13295             this.onTouchViewEmptyResults();
13296             return;
13297         }
13298         
13299         this.clearTouchView();
13300         
13301         var rawValue = this.getRawValue();
13302         
13303         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13304         
13305         this.tickItems = [];
13306         
13307         this.store.data.each(function(d, rowIndex){
13308             var row = this.touchViewListGroup.createChild(template);
13309             
13310             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13311                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13312             }
13313             
13314             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13315                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13316             }
13317             
13318             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13319                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13320                 this.tickItems.push(d.data);
13321             }
13322             
13323             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13324             
13325         }, this);
13326         
13327         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13328         
13329         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13330
13331         if(this.fieldLabel.length){
13332             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13333         }
13334
13335         var listHeight = this.touchViewListGroup.getHeight();
13336         
13337         if(firstChecked && listHeight > bodyHeight){
13338             (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13339         }
13340         
13341     },
13342     
13343     onTouchViewLoadException : function()
13344     {
13345         this.hideTouchView();
13346     },
13347     
13348     onTouchViewEmptyResults : function()
13349     {
13350         this.clearTouchView();
13351         
13352         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13353         
13354         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13355         
13356     },
13357     
13358     clearTouchView : function()
13359     {
13360         this.touchViewListGroup.dom.innerHTML = '';
13361     },
13362     
13363     onTouchViewClick : function(e, el, o)
13364     {
13365         e.preventDefault();
13366         
13367         var row = o.row;
13368         var rowIndex = o.rowIndex;
13369         
13370         var r = this.store.getAt(rowIndex);
13371         
13372         if(!this.multiple){
13373             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13374                 c.dom.removeAttribute('checked');
13375             }, this);
13376             
13377             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13378         
13379             this.setFromData(r.data);
13380             
13381             var close = this.closeTriggerEl();
13382         
13383             if(close){
13384                 close.show();
13385             }
13386
13387             this.hideTouchView();
13388             
13389             this.fireEvent('select', this, r, rowIndex);
13390             
13391             return;
13392         }
13393         
13394         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13395             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13396             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13397             return;
13398         }
13399         
13400         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13401         this.addItem(r.data);
13402         this.tickItems.push(r.data);
13403         
13404     }
13405     
13406
13407     /** 
13408     * @cfg {Boolean} grow 
13409     * @hide 
13410     */
13411     /** 
13412     * @cfg {Number} growMin 
13413     * @hide 
13414     */
13415     /** 
13416     * @cfg {Number} growMax 
13417     * @hide 
13418     */
13419     /**
13420      * @hide
13421      * @method autoSize
13422      */
13423 });
13424
13425 Roo.apply(Roo.bootstrap.ComboBox,  {
13426     
13427     header : {
13428         tag: 'div',
13429         cls: 'modal-header',
13430         cn: [
13431             {
13432                 tag: 'h4',
13433                 cls: 'modal-title'
13434             }
13435         ]
13436     },
13437     
13438     body : {
13439         tag: 'div',
13440         cls: 'modal-body',
13441         cn: [
13442             {
13443                 tag: 'ul',
13444                 cls: 'list-group'
13445             }
13446         ]
13447     },
13448     
13449     listItemRadio : {
13450         tag: 'li',
13451         cls: 'list-group-item',
13452         cn: [
13453             {
13454                 tag: 'span',
13455                 cls: 'roo-combobox-list-group-item-value'
13456             },
13457             {
13458                 tag: 'div',
13459                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13460                 cn: [
13461                     {
13462                         tag: 'input',
13463                         type: 'radio'
13464                     },
13465                     {
13466                         tag: 'label'
13467                     }
13468                 ]
13469             }
13470         ]
13471     },
13472     
13473     listItemCheckbox : {
13474         tag: 'li',
13475         cls: 'list-group-item',
13476         cn: [
13477             {
13478                 tag: 'span',
13479                 cls: 'roo-combobox-list-group-item-value'
13480             },
13481             {
13482                 tag: 'div',
13483                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13484                 cn: [
13485                     {
13486                         tag: 'input',
13487                         type: 'checkbox'
13488                     },
13489                     {
13490                         tag: 'label'
13491                     }
13492                 ]
13493             }
13494         ]
13495     },
13496     
13497     emptyResult : {
13498         tag: 'div',
13499         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13500     },
13501     
13502     footer : {
13503         tag: 'div',
13504         cls: 'modal-footer',
13505         cn: [
13506             {
13507                 tag: 'div',
13508                 cls: 'row',
13509                 cn: [
13510                     {
13511                         tag: 'div',
13512                         cls: 'col-xs-6 text-left',
13513                         cn: {
13514                             tag: 'button',
13515                             cls: 'btn btn-danger roo-touch-view-cancel',
13516                             html: 'Cancel'
13517                         }
13518                     },
13519                     {
13520                         tag: 'div',
13521                         cls: 'col-xs-6 text-right',
13522                         cn: {
13523                             tag: 'button',
13524                             cls: 'btn btn-success roo-touch-view-ok',
13525                             html: 'OK'
13526                         }
13527                     }
13528                 ]
13529             }
13530         ]
13531         
13532     }
13533 });
13534
13535 Roo.apply(Roo.bootstrap.ComboBox,  {
13536     
13537     touchViewTemplate : {
13538         tag: 'div',
13539         cls: 'modal fade roo-combobox-touch-view',
13540         cn: [
13541             {
13542                 tag: 'div',
13543                 cls: 'modal-dialog',
13544                 cn: [
13545                     {
13546                         tag: 'div',
13547                         cls: 'modal-content',
13548                         cn: [
13549                             Roo.bootstrap.ComboBox.header,
13550                             Roo.bootstrap.ComboBox.body,
13551                             Roo.bootstrap.ComboBox.footer
13552                         ]
13553                     }
13554                 ]
13555             }
13556         ]
13557     }
13558 });/*
13559  * Based on:
13560  * Ext JS Library 1.1.1
13561  * Copyright(c) 2006-2007, Ext JS, LLC.
13562  *
13563  * Originally Released Under LGPL - original licence link has changed is not relivant.
13564  *
13565  * Fork - LGPL
13566  * <script type="text/javascript">
13567  */
13568
13569 /**
13570  * @class Roo.View
13571  * @extends Roo.util.Observable
13572  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13573  * This class also supports single and multi selection modes. <br>
13574  * Create a data model bound view:
13575  <pre><code>
13576  var store = new Roo.data.Store(...);
13577
13578  var view = new Roo.View({
13579     el : "my-element",
13580     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13581  
13582     singleSelect: true,
13583     selectedClass: "ydataview-selected",
13584     store: store
13585  });
13586
13587  // listen for node click?
13588  view.on("click", function(vw, index, node, e){
13589  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13590  });
13591
13592  // load XML data
13593  dataModel.load("foobar.xml");
13594  </code></pre>
13595  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13596  * <br><br>
13597  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13598  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13599  * 
13600  * Note: old style constructor is still suported (container, template, config)
13601  * 
13602  * @constructor
13603  * Create a new View
13604  * @param {Object} config The config object
13605  * 
13606  */
13607 Roo.View = function(config, depreciated_tpl, depreciated_config){
13608     
13609     this.parent = false;
13610     
13611     if (typeof(depreciated_tpl) == 'undefined') {
13612         // new way.. - universal constructor.
13613         Roo.apply(this, config);
13614         this.el  = Roo.get(this.el);
13615     } else {
13616         // old format..
13617         this.el  = Roo.get(config);
13618         this.tpl = depreciated_tpl;
13619         Roo.apply(this, depreciated_config);
13620     }
13621     this.wrapEl  = this.el.wrap().wrap();
13622     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13623     
13624     
13625     if(typeof(this.tpl) == "string"){
13626         this.tpl = new Roo.Template(this.tpl);
13627     } else {
13628         // support xtype ctors..
13629         this.tpl = new Roo.factory(this.tpl, Roo);
13630     }
13631     
13632     
13633     this.tpl.compile();
13634     
13635     /** @private */
13636     this.addEvents({
13637         /**
13638          * @event beforeclick
13639          * Fires before a click is processed. Returns false to cancel the default action.
13640          * @param {Roo.View} this
13641          * @param {Number} index The index of the target node
13642          * @param {HTMLElement} node The target node
13643          * @param {Roo.EventObject} e The raw event object
13644          */
13645             "beforeclick" : true,
13646         /**
13647          * @event click
13648          * Fires when a template node is clicked.
13649          * @param {Roo.View} this
13650          * @param {Number} index The index of the target node
13651          * @param {HTMLElement} node The target node
13652          * @param {Roo.EventObject} e The raw event object
13653          */
13654             "click" : true,
13655         /**
13656          * @event dblclick
13657          * Fires when a template node is double clicked.
13658          * @param {Roo.View} this
13659          * @param {Number} index The index of the target node
13660          * @param {HTMLElement} node The target node
13661          * @param {Roo.EventObject} e The raw event object
13662          */
13663             "dblclick" : true,
13664         /**
13665          * @event contextmenu
13666          * Fires when a template node is right clicked.
13667          * @param {Roo.View} this
13668          * @param {Number} index The index of the target node
13669          * @param {HTMLElement} node The target node
13670          * @param {Roo.EventObject} e The raw event object
13671          */
13672             "contextmenu" : true,
13673         /**
13674          * @event selectionchange
13675          * Fires when the selected nodes change.
13676          * @param {Roo.View} this
13677          * @param {Array} selections Array of the selected nodes
13678          */
13679             "selectionchange" : true,
13680     
13681         /**
13682          * @event beforeselect
13683          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13684          * @param {Roo.View} this
13685          * @param {HTMLElement} node The node to be selected
13686          * @param {Array} selections Array of currently selected nodes
13687          */
13688             "beforeselect" : true,
13689         /**
13690          * @event preparedata
13691          * Fires on every row to render, to allow you to change the data.
13692          * @param {Roo.View} this
13693          * @param {Object} data to be rendered (change this)
13694          */
13695           "preparedata" : true
13696           
13697           
13698         });
13699
13700
13701
13702     this.el.on({
13703         "click": this.onClick,
13704         "dblclick": this.onDblClick,
13705         "contextmenu": this.onContextMenu,
13706         scope:this
13707     });
13708
13709     this.selections = [];
13710     this.nodes = [];
13711     this.cmp = new Roo.CompositeElementLite([]);
13712     if(this.store){
13713         this.store = Roo.factory(this.store, Roo.data);
13714         this.setStore(this.store, true);
13715     }
13716     
13717     if ( this.footer && this.footer.xtype) {
13718            
13719          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13720         
13721         this.footer.dataSource = this.store
13722         this.footer.container = fctr;
13723         this.footer = Roo.factory(this.footer, Roo);
13724         fctr.insertFirst(this.el);
13725         
13726         // this is a bit insane - as the paging toolbar seems to detach the el..
13727 //        dom.parentNode.parentNode.parentNode
13728          // they get detached?
13729     }
13730     
13731     
13732     Roo.View.superclass.constructor.call(this);
13733     
13734     
13735 };
13736
13737 Roo.extend(Roo.View, Roo.util.Observable, {
13738     
13739      /**
13740      * @cfg {Roo.data.Store} store Data store to load data from.
13741      */
13742     store : false,
13743     
13744     /**
13745      * @cfg {String|Roo.Element} el The container element.
13746      */
13747     el : '',
13748     
13749     /**
13750      * @cfg {String|Roo.Template} tpl The template used by this View 
13751      */
13752     tpl : false,
13753     /**
13754      * @cfg {String} dataName the named area of the template to use as the data area
13755      *                          Works with domtemplates roo-name="name"
13756      */
13757     dataName: false,
13758     /**
13759      * @cfg {String} selectedClass The css class to add to selected nodes
13760      */
13761     selectedClass : "x-view-selected",
13762      /**
13763      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13764      */
13765     emptyText : "",
13766     
13767     /**
13768      * @cfg {String} text to display on mask (default Loading)
13769      */
13770     mask : false,
13771     /**
13772      * @cfg {Boolean} multiSelect Allow multiple selection
13773      */
13774     multiSelect : false,
13775     /**
13776      * @cfg {Boolean} singleSelect Allow single selection
13777      */
13778     singleSelect:  false,
13779     
13780     /**
13781      * @cfg {Boolean} toggleSelect - selecting 
13782      */
13783     toggleSelect : false,
13784     
13785     /**
13786      * @cfg {Boolean} tickable - selecting 
13787      */
13788     tickable : false,
13789     
13790     /**
13791      * Returns the element this view is bound to.
13792      * @return {Roo.Element}
13793      */
13794     getEl : function(){
13795         return this.wrapEl;
13796     },
13797     
13798     
13799
13800     /**
13801      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13802      */
13803     refresh : function(){
13804         //Roo.log('refresh');
13805         var t = this.tpl;
13806         
13807         // if we are using something like 'domtemplate', then
13808         // the what gets used is:
13809         // t.applySubtemplate(NAME, data, wrapping data..)
13810         // the outer template then get' applied with
13811         //     the store 'extra data'
13812         // and the body get's added to the
13813         //      roo-name="data" node?
13814         //      <span class='roo-tpl-{name}'></span> ?????
13815         
13816         
13817         
13818         this.clearSelections();
13819         this.el.update("");
13820         var html = [];
13821         var records = this.store.getRange();
13822         if(records.length < 1) {
13823             
13824             // is this valid??  = should it render a template??
13825             
13826             this.el.update(this.emptyText);
13827             return;
13828         }
13829         var el = this.el;
13830         if (this.dataName) {
13831             this.el.update(t.apply(this.store.meta)); //????
13832             el = this.el.child('.roo-tpl-' + this.dataName);
13833         }
13834         
13835         for(var i = 0, len = records.length; i < len; i++){
13836             var data = this.prepareData(records[i].data, i, records[i]);
13837             this.fireEvent("preparedata", this, data, i, records[i]);
13838             
13839             var d = Roo.apply({}, data);
13840             
13841             if(this.tickable){
13842                 Roo.apply(d, {'roo-id' : Roo.id()});
13843                 
13844                 var _this = this;
13845             
13846                 Roo.each(this.parent.item, function(item){
13847                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13848                         return;
13849                     }
13850                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13851                 });
13852             }
13853             
13854             html[html.length] = Roo.util.Format.trim(
13855                 this.dataName ?
13856                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13857                     t.apply(d)
13858             );
13859         }
13860         
13861         
13862         
13863         el.update(html.join(""));
13864         this.nodes = el.dom.childNodes;
13865         this.updateIndexes(0);
13866     },
13867     
13868
13869     /**
13870      * Function to override to reformat the data that is sent to
13871      * the template for each node.
13872      * DEPRICATED - use the preparedata event handler.
13873      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13874      * a JSON object for an UpdateManager bound view).
13875      */
13876     prepareData : function(data, index, record)
13877     {
13878         this.fireEvent("preparedata", this, data, index, record);
13879         return data;
13880     },
13881
13882     onUpdate : function(ds, record){
13883         // Roo.log('on update');   
13884         this.clearSelections();
13885         var index = this.store.indexOf(record);
13886         var n = this.nodes[index];
13887         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13888         n.parentNode.removeChild(n);
13889         this.updateIndexes(index, index);
13890     },
13891
13892     
13893     
13894 // --------- FIXME     
13895     onAdd : function(ds, records, index)
13896     {
13897         //Roo.log(['on Add', ds, records, index] );        
13898         this.clearSelections();
13899         if(this.nodes.length == 0){
13900             this.refresh();
13901             return;
13902         }
13903         var n = this.nodes[index];
13904         for(var i = 0, len = records.length; i < len; i++){
13905             var d = this.prepareData(records[i].data, i, records[i]);
13906             if(n){
13907                 this.tpl.insertBefore(n, d);
13908             }else{
13909                 
13910                 this.tpl.append(this.el, d);
13911             }
13912         }
13913         this.updateIndexes(index);
13914     },
13915
13916     onRemove : function(ds, record, index){
13917        // Roo.log('onRemove');
13918         this.clearSelections();
13919         var el = this.dataName  ?
13920             this.el.child('.roo-tpl-' + this.dataName) :
13921             this.el; 
13922         
13923         el.dom.removeChild(this.nodes[index]);
13924         this.updateIndexes(index);
13925     },
13926
13927     /**
13928      * Refresh an individual node.
13929      * @param {Number} index
13930      */
13931     refreshNode : function(index){
13932         this.onUpdate(this.store, this.store.getAt(index));
13933     },
13934
13935     updateIndexes : function(startIndex, endIndex){
13936         var ns = this.nodes;
13937         startIndex = startIndex || 0;
13938         endIndex = endIndex || ns.length - 1;
13939         for(var i = startIndex; i <= endIndex; i++){
13940             ns[i].nodeIndex = i;
13941         }
13942     },
13943
13944     /**
13945      * Changes the data store this view uses and refresh the view.
13946      * @param {Store} store
13947      */
13948     setStore : function(store, initial){
13949         if(!initial && this.store){
13950             this.store.un("datachanged", this.refresh);
13951             this.store.un("add", this.onAdd);
13952             this.store.un("remove", this.onRemove);
13953             this.store.un("update", this.onUpdate);
13954             this.store.un("clear", this.refresh);
13955             this.store.un("beforeload", this.onBeforeLoad);
13956             this.store.un("load", this.onLoad);
13957             this.store.un("loadexception", this.onLoad);
13958         }
13959         if(store){
13960           
13961             store.on("datachanged", this.refresh, this);
13962             store.on("add", this.onAdd, this);
13963             store.on("remove", this.onRemove, this);
13964             store.on("update", this.onUpdate, this);
13965             store.on("clear", this.refresh, this);
13966             store.on("beforeload", this.onBeforeLoad, this);
13967             store.on("load", this.onLoad, this);
13968             store.on("loadexception", this.onLoad, this);
13969         }
13970         
13971         if(store){
13972             this.refresh();
13973         }
13974     },
13975     /**
13976      * onbeforeLoad - masks the loading area.
13977      *
13978      */
13979     onBeforeLoad : function(store,opts)
13980     {
13981          //Roo.log('onBeforeLoad');   
13982         if (!opts.add) {
13983             this.el.update("");
13984         }
13985         this.el.mask(this.mask ? this.mask : "Loading" ); 
13986     },
13987     onLoad : function ()
13988     {
13989         this.el.unmask();
13990     },
13991     
13992
13993     /**
13994      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13995      * @param {HTMLElement} node
13996      * @return {HTMLElement} The template node
13997      */
13998     findItemFromChild : function(node){
13999         var el = this.dataName  ?
14000             this.el.child('.roo-tpl-' + this.dataName,true) :
14001             this.el.dom; 
14002         
14003         if(!node || node.parentNode == el){
14004                     return node;
14005             }
14006             var p = node.parentNode;
14007             while(p && p != el){
14008             if(p.parentNode == el){
14009                 return p;
14010             }
14011             p = p.parentNode;
14012         }
14013             return null;
14014     },
14015
14016     /** @ignore */
14017     onClick : function(e){
14018         var item = this.findItemFromChild(e.getTarget());
14019         if(item){
14020             var index = this.indexOf(item);
14021             if(this.onItemClick(item, index, e) !== false){
14022                 this.fireEvent("click", this, index, item, e);
14023             }
14024         }else{
14025             this.clearSelections();
14026         }
14027     },
14028
14029     /** @ignore */
14030     onContextMenu : function(e){
14031         var item = this.findItemFromChild(e.getTarget());
14032         if(item){
14033             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14034         }
14035     },
14036
14037     /** @ignore */
14038     onDblClick : function(e){
14039         var item = this.findItemFromChild(e.getTarget());
14040         if(item){
14041             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14042         }
14043     },
14044
14045     onItemClick : function(item, index, e)
14046     {
14047         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14048             return false;
14049         }
14050         if (this.toggleSelect) {
14051             var m = this.isSelected(item) ? 'unselect' : 'select';
14052             //Roo.log(m);
14053             var _t = this;
14054             _t[m](item, true, false);
14055             return true;
14056         }
14057         if(this.multiSelect || this.singleSelect){
14058             if(this.multiSelect && e.shiftKey && this.lastSelection){
14059                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14060             }else{
14061                 this.select(item, this.multiSelect && e.ctrlKey);
14062                 this.lastSelection = item;
14063             }
14064             
14065             if(!this.tickable){
14066                 e.preventDefault();
14067             }
14068             
14069         }
14070         return true;
14071     },
14072
14073     /**
14074      * Get the number of selected nodes.
14075      * @return {Number}
14076      */
14077     getSelectionCount : function(){
14078         return this.selections.length;
14079     },
14080
14081     /**
14082      * Get the currently selected nodes.
14083      * @return {Array} An array of HTMLElements
14084      */
14085     getSelectedNodes : function(){
14086         return this.selections;
14087     },
14088
14089     /**
14090      * Get the indexes of the selected nodes.
14091      * @return {Array}
14092      */
14093     getSelectedIndexes : function(){
14094         var indexes = [], s = this.selections;
14095         for(var i = 0, len = s.length; i < len; i++){
14096             indexes.push(s[i].nodeIndex);
14097         }
14098         return indexes;
14099     },
14100
14101     /**
14102      * Clear all selections
14103      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14104      */
14105     clearSelections : function(suppressEvent){
14106         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14107             this.cmp.elements = this.selections;
14108             this.cmp.removeClass(this.selectedClass);
14109             this.selections = [];
14110             if(!suppressEvent){
14111                 this.fireEvent("selectionchange", this, this.selections);
14112             }
14113         }
14114     },
14115
14116     /**
14117      * Returns true if the passed node is selected
14118      * @param {HTMLElement/Number} node The node or node index
14119      * @return {Boolean}
14120      */
14121     isSelected : function(node){
14122         var s = this.selections;
14123         if(s.length < 1){
14124             return false;
14125         }
14126         node = this.getNode(node);
14127         return s.indexOf(node) !== -1;
14128     },
14129
14130     /**
14131      * Selects nodes.
14132      * @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
14133      * @param {Boolean} keepExisting (optional) true to keep existing selections
14134      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14135      */
14136     select : function(nodeInfo, keepExisting, suppressEvent){
14137         if(nodeInfo instanceof Array){
14138             if(!keepExisting){
14139                 this.clearSelections(true);
14140             }
14141             for(var i = 0, len = nodeInfo.length; i < len; i++){
14142                 this.select(nodeInfo[i], true, true);
14143             }
14144             return;
14145         } 
14146         var node = this.getNode(nodeInfo);
14147         if(!node || this.isSelected(node)){
14148             return; // already selected.
14149         }
14150         if(!keepExisting){
14151             this.clearSelections(true);
14152         }
14153         
14154         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14155             Roo.fly(node).addClass(this.selectedClass);
14156             this.selections.push(node);
14157             if(!suppressEvent){
14158                 this.fireEvent("selectionchange", this, this.selections);
14159             }
14160         }
14161         
14162         
14163     },
14164       /**
14165      * Unselects nodes.
14166      * @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
14167      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14168      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14169      */
14170     unselect : function(nodeInfo, keepExisting, suppressEvent)
14171     {
14172         if(nodeInfo instanceof Array){
14173             Roo.each(this.selections, function(s) {
14174                 this.unselect(s, nodeInfo);
14175             }, this);
14176             return;
14177         }
14178         var node = this.getNode(nodeInfo);
14179         if(!node || !this.isSelected(node)){
14180             //Roo.log("not selected");
14181             return; // not selected.
14182         }
14183         // fireevent???
14184         var ns = [];
14185         Roo.each(this.selections, function(s) {
14186             if (s == node ) {
14187                 Roo.fly(node).removeClass(this.selectedClass);
14188
14189                 return;
14190             }
14191             ns.push(s);
14192         },this);
14193         
14194         this.selections= ns;
14195         this.fireEvent("selectionchange", this, this.selections);
14196     },
14197
14198     /**
14199      * Gets a template node.
14200      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14201      * @return {HTMLElement} The node or null if it wasn't found
14202      */
14203     getNode : function(nodeInfo){
14204         if(typeof nodeInfo == "string"){
14205             return document.getElementById(nodeInfo);
14206         }else if(typeof nodeInfo == "number"){
14207             return this.nodes[nodeInfo];
14208         }
14209         return nodeInfo;
14210     },
14211
14212     /**
14213      * Gets a range template nodes.
14214      * @param {Number} startIndex
14215      * @param {Number} endIndex
14216      * @return {Array} An array of nodes
14217      */
14218     getNodes : function(start, end){
14219         var ns = this.nodes;
14220         start = start || 0;
14221         end = typeof end == "undefined" ? ns.length - 1 : end;
14222         var nodes = [];
14223         if(start <= end){
14224             for(var i = start; i <= end; i++){
14225                 nodes.push(ns[i]);
14226             }
14227         } else{
14228             for(var i = start; i >= end; i--){
14229                 nodes.push(ns[i]);
14230             }
14231         }
14232         return nodes;
14233     },
14234
14235     /**
14236      * Finds the index of the passed node
14237      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14238      * @return {Number} The index of the node or -1
14239      */
14240     indexOf : function(node){
14241         node = this.getNode(node);
14242         if(typeof node.nodeIndex == "number"){
14243             return node.nodeIndex;
14244         }
14245         var ns = this.nodes;
14246         for(var i = 0, len = ns.length; i < len; i++){
14247             if(ns[i] == node){
14248                 return i;
14249             }
14250         }
14251         return -1;
14252     }
14253 });
14254 /*
14255  * - LGPL
14256  *
14257  * based on jquery fullcalendar
14258  * 
14259  */
14260
14261 Roo.bootstrap = Roo.bootstrap || {};
14262 /**
14263  * @class Roo.bootstrap.Calendar
14264  * @extends Roo.bootstrap.Component
14265  * Bootstrap Calendar class
14266  * @cfg {Boolean} loadMask (true|false) default false
14267  * @cfg {Object} header generate the user specific header of the calendar, default false
14268
14269  * @constructor
14270  * Create a new Container
14271  * @param {Object} config The config object
14272  */
14273
14274
14275
14276 Roo.bootstrap.Calendar = function(config){
14277     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14278      this.addEvents({
14279         /**
14280              * @event select
14281              * Fires when a date is selected
14282              * @param {DatePicker} this
14283              * @param {Date} date The selected date
14284              */
14285         'select': true,
14286         /**
14287              * @event monthchange
14288              * Fires when the displayed month changes 
14289              * @param {DatePicker} this
14290              * @param {Date} date The selected month
14291              */
14292         'monthchange': true,
14293         /**
14294              * @event evententer
14295              * Fires when mouse over an event
14296              * @param {Calendar} this
14297              * @param {event} Event
14298              */
14299         'evententer': true,
14300         /**
14301              * @event eventleave
14302              * Fires when the mouse leaves an
14303              * @param {Calendar} this
14304              * @param {event}
14305              */
14306         'eventleave': true,
14307         /**
14308              * @event eventclick
14309              * Fires when the mouse click an
14310              * @param {Calendar} this
14311              * @param {event}
14312              */
14313         'eventclick': true
14314         
14315     });
14316
14317 };
14318
14319 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14320     
14321      /**
14322      * @cfg {Number} startDay
14323      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14324      */
14325     startDay : 0,
14326     
14327     loadMask : false,
14328     
14329     header : false,
14330       
14331     getAutoCreate : function(){
14332         
14333         
14334         var fc_button = function(name, corner, style, content ) {
14335             return Roo.apply({},{
14336                 tag : 'span',
14337                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14338                          (corner.length ?
14339                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14340                             ''
14341                         ),
14342                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14343                 unselectable: 'on'
14344             });
14345         };
14346         
14347         var header = {};
14348         
14349         if(!this.header){
14350             header = {
14351                 tag : 'table',
14352                 cls : 'fc-header',
14353                 style : 'width:100%',
14354                 cn : [
14355                     {
14356                         tag: 'tr',
14357                         cn : [
14358                             {
14359                                 tag : 'td',
14360                                 cls : 'fc-header-left',
14361                                 cn : [
14362                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14363                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14364                                     { tag: 'span', cls: 'fc-header-space' },
14365                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14366
14367
14368                                 ]
14369                             },
14370
14371                             {
14372                                 tag : 'td',
14373                                 cls : 'fc-header-center',
14374                                 cn : [
14375                                     {
14376                                         tag: 'span',
14377                                         cls: 'fc-header-title',
14378                                         cn : {
14379                                             tag: 'H2',
14380                                             html : 'month / year'
14381                                         }
14382                                     }
14383
14384                                 ]
14385                             },
14386                             {
14387                                 tag : 'td',
14388                                 cls : 'fc-header-right',
14389                                 cn : [
14390                               /*      fc_button('month', 'left', '', 'month' ),
14391                                     fc_button('week', '', '', 'week' ),
14392                                     fc_button('day', 'right', '', 'day' )
14393                                 */    
14394
14395                                 ]
14396                             }
14397
14398                         ]
14399                     }
14400                 ]
14401             };
14402         }
14403         
14404         header = this.header;
14405         
14406        
14407         var cal_heads = function() {
14408             var ret = [];
14409             // fixme - handle this.
14410             
14411             for (var i =0; i < Date.dayNames.length; i++) {
14412                 var d = Date.dayNames[i];
14413                 ret.push({
14414                     tag: 'th',
14415                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14416                     html : d.substring(0,3)
14417                 });
14418                 
14419             }
14420             ret[0].cls += ' fc-first';
14421             ret[6].cls += ' fc-last';
14422             return ret;
14423         };
14424         var cal_cell = function(n) {
14425             return  {
14426                 tag: 'td',
14427                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14428                 cn : [
14429                     {
14430                         cn : [
14431                             {
14432                                 cls: 'fc-day-number',
14433                                 html: 'D'
14434                             },
14435                             {
14436                                 cls: 'fc-day-content',
14437                              
14438                                 cn : [
14439                                      {
14440                                         style: 'position: relative;' // height: 17px;
14441                                     }
14442                                 ]
14443                             }
14444                             
14445                             
14446                         ]
14447                     }
14448                 ]
14449                 
14450             }
14451         };
14452         var cal_rows = function() {
14453             
14454             var ret = [];
14455             for (var r = 0; r < 6; r++) {
14456                 var row= {
14457                     tag : 'tr',
14458                     cls : 'fc-week',
14459                     cn : []
14460                 };
14461                 
14462                 for (var i =0; i < Date.dayNames.length; i++) {
14463                     var d = Date.dayNames[i];
14464                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14465
14466                 }
14467                 row.cn[0].cls+=' fc-first';
14468                 row.cn[0].cn[0].style = 'min-height:90px';
14469                 row.cn[6].cls+=' fc-last';
14470                 ret.push(row);
14471                 
14472             }
14473             ret[0].cls += ' fc-first';
14474             ret[4].cls += ' fc-prev-last';
14475             ret[5].cls += ' fc-last';
14476             return ret;
14477             
14478         };
14479         
14480         var cal_table = {
14481             tag: 'table',
14482             cls: 'fc-border-separate',
14483             style : 'width:100%',
14484             cellspacing  : 0,
14485             cn : [
14486                 { 
14487                     tag: 'thead',
14488                     cn : [
14489                         { 
14490                             tag: 'tr',
14491                             cls : 'fc-first fc-last',
14492                             cn : cal_heads()
14493                         }
14494                     ]
14495                 },
14496                 { 
14497                     tag: 'tbody',
14498                     cn : cal_rows()
14499                 }
14500                   
14501             ]
14502         };
14503          
14504          var cfg = {
14505             cls : 'fc fc-ltr',
14506             cn : [
14507                 header,
14508                 {
14509                     cls : 'fc-content',
14510                     style : "position: relative;",
14511                     cn : [
14512                         {
14513                             cls : 'fc-view fc-view-month fc-grid',
14514                             style : 'position: relative',
14515                             unselectable : 'on',
14516                             cn : [
14517                                 {
14518                                     cls : 'fc-event-container',
14519                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14520                                 },
14521                                 cal_table
14522                             ]
14523                         }
14524                     ]
14525     
14526                 }
14527            ] 
14528             
14529         };
14530         
14531          
14532         
14533         return cfg;
14534     },
14535     
14536     
14537     initEvents : function()
14538     {
14539         if(!this.store){
14540             throw "can not find store for calendar";
14541         }
14542         
14543         var mark = {
14544             tag: "div",
14545             cls:"x-dlg-mask",
14546             style: "text-align:center",
14547             cn: [
14548                 {
14549                     tag: "div",
14550                     style: "background-color:white;width:50%;margin:250 auto",
14551                     cn: [
14552                         {
14553                             tag: "img",
14554                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14555                         },
14556                         {
14557                             tag: "span",
14558                             html: "Loading"
14559                         }
14560                         
14561                     ]
14562                 }
14563             ]
14564         }
14565         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14566         
14567         var size = this.el.select('.fc-content', true).first().getSize();
14568         this.maskEl.setSize(size.width, size.height);
14569         this.maskEl.enableDisplayMode("block");
14570         if(!this.loadMask){
14571             this.maskEl.hide();
14572         }
14573         
14574         this.store = Roo.factory(this.store, Roo.data);
14575         this.store.on('load', this.onLoad, this);
14576         this.store.on('beforeload', this.onBeforeLoad, this);
14577         
14578         this.resize();
14579         
14580         this.cells = this.el.select('.fc-day',true);
14581         //Roo.log(this.cells);
14582         this.textNodes = this.el.query('.fc-day-number');
14583         this.cells.addClassOnOver('fc-state-hover');
14584         
14585         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14586         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14587         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14588         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14589         
14590         this.on('monthchange', this.onMonthChange, this);
14591         
14592         this.update(new Date().clearTime());
14593     },
14594     
14595     resize : function() {
14596         var sz  = this.el.getSize();
14597         
14598         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14599         this.el.select('.fc-day-content div',true).setHeight(34);
14600     },
14601     
14602     
14603     // private
14604     showPrevMonth : function(e){
14605         this.update(this.activeDate.add("mo", -1));
14606     },
14607     showToday : function(e){
14608         this.update(new Date().clearTime());
14609     },
14610     // private
14611     showNextMonth : function(e){
14612         this.update(this.activeDate.add("mo", 1));
14613     },
14614
14615     // private
14616     showPrevYear : function(){
14617         this.update(this.activeDate.add("y", -1));
14618     },
14619
14620     // private
14621     showNextYear : function(){
14622         this.update(this.activeDate.add("y", 1));
14623     },
14624
14625     
14626    // private
14627     update : function(date)
14628     {
14629         var vd = this.activeDate;
14630         this.activeDate = date;
14631 //        if(vd && this.el){
14632 //            var t = date.getTime();
14633 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14634 //                Roo.log('using add remove');
14635 //                
14636 //                this.fireEvent('monthchange', this, date);
14637 //                
14638 //                this.cells.removeClass("fc-state-highlight");
14639 //                this.cells.each(function(c){
14640 //                   if(c.dateValue == t){
14641 //                       c.addClass("fc-state-highlight");
14642 //                       setTimeout(function(){
14643 //                            try{c.dom.firstChild.focus();}catch(e){}
14644 //                       }, 50);
14645 //                       return false;
14646 //                   }
14647 //                   return true;
14648 //                });
14649 //                return;
14650 //            }
14651 //        }
14652         
14653         var days = date.getDaysInMonth();
14654         
14655         var firstOfMonth = date.getFirstDateOfMonth();
14656         var startingPos = firstOfMonth.getDay()-this.startDay;
14657         
14658         if(startingPos < this.startDay){
14659             startingPos += 7;
14660         }
14661         
14662         var pm = date.add(Date.MONTH, -1);
14663         var prevStart = pm.getDaysInMonth()-startingPos;
14664 //        
14665         this.cells = this.el.select('.fc-day',true);
14666         this.textNodes = this.el.query('.fc-day-number');
14667         this.cells.addClassOnOver('fc-state-hover');
14668         
14669         var cells = this.cells.elements;
14670         var textEls = this.textNodes;
14671         
14672         Roo.each(cells, function(cell){
14673             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14674         });
14675         
14676         days += startingPos;
14677
14678         // convert everything to numbers so it's fast
14679         var day = 86400000;
14680         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14681         //Roo.log(d);
14682         //Roo.log(pm);
14683         //Roo.log(prevStart);
14684         
14685         var today = new Date().clearTime().getTime();
14686         var sel = date.clearTime().getTime();
14687         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14688         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14689         var ddMatch = this.disabledDatesRE;
14690         var ddText = this.disabledDatesText;
14691         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14692         var ddaysText = this.disabledDaysText;
14693         var format = this.format;
14694         
14695         var setCellClass = function(cal, cell){
14696             cell.row = 0;
14697             cell.events = [];
14698             cell.more = [];
14699             //Roo.log('set Cell Class');
14700             cell.title = "";
14701             var t = d.getTime();
14702             
14703             //Roo.log(d);
14704             
14705             cell.dateValue = t;
14706             if(t == today){
14707                 cell.className += " fc-today";
14708                 cell.className += " fc-state-highlight";
14709                 cell.title = cal.todayText;
14710             }
14711             if(t == sel){
14712                 // disable highlight in other month..
14713                 //cell.className += " fc-state-highlight";
14714                 
14715             }
14716             // disabling
14717             if(t < min) {
14718                 cell.className = " fc-state-disabled";
14719                 cell.title = cal.minText;
14720                 return;
14721             }
14722             if(t > max) {
14723                 cell.className = " fc-state-disabled";
14724                 cell.title = cal.maxText;
14725                 return;
14726             }
14727             if(ddays){
14728                 if(ddays.indexOf(d.getDay()) != -1){
14729                     cell.title = ddaysText;
14730                     cell.className = " fc-state-disabled";
14731                 }
14732             }
14733             if(ddMatch && format){
14734                 var fvalue = d.dateFormat(format);
14735                 if(ddMatch.test(fvalue)){
14736                     cell.title = ddText.replace("%0", fvalue);
14737                     cell.className = " fc-state-disabled";
14738                 }
14739             }
14740             
14741             if (!cell.initialClassName) {
14742                 cell.initialClassName = cell.dom.className;
14743             }
14744             
14745             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14746         };
14747
14748         var i = 0;
14749         
14750         for(; i < startingPos; i++) {
14751             textEls[i].innerHTML = (++prevStart);
14752             d.setDate(d.getDate()+1);
14753             
14754             cells[i].className = "fc-past fc-other-month";
14755             setCellClass(this, cells[i]);
14756         }
14757         
14758         var intDay = 0;
14759         
14760         for(; i < days; i++){
14761             intDay = i - startingPos + 1;
14762             textEls[i].innerHTML = (intDay);
14763             d.setDate(d.getDate()+1);
14764             
14765             cells[i].className = ''; // "x-date-active";
14766             setCellClass(this, cells[i]);
14767         }
14768         var extraDays = 0;
14769         
14770         for(; i < 42; i++) {
14771             textEls[i].innerHTML = (++extraDays);
14772             d.setDate(d.getDate()+1);
14773             
14774             cells[i].className = "fc-future fc-other-month";
14775             setCellClass(this, cells[i]);
14776         }
14777         
14778         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14779         
14780         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14781         
14782         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14783         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14784         
14785         if(totalRows != 6){
14786             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14787             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14788         }
14789         
14790         this.fireEvent('monthchange', this, date);
14791         
14792         
14793         /*
14794         if(!this.internalRender){
14795             var main = this.el.dom.firstChild;
14796             var w = main.offsetWidth;
14797             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14798             Roo.fly(main).setWidth(w);
14799             this.internalRender = true;
14800             // opera does not respect the auto grow header center column
14801             // then, after it gets a width opera refuses to recalculate
14802             // without a second pass
14803             if(Roo.isOpera && !this.secondPass){
14804                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14805                 this.secondPass = true;
14806                 this.update.defer(10, this, [date]);
14807             }
14808         }
14809         */
14810         
14811     },
14812     
14813     findCell : function(dt) {
14814         dt = dt.clearTime().getTime();
14815         var ret = false;
14816         this.cells.each(function(c){
14817             //Roo.log("check " +c.dateValue + '?=' + dt);
14818             if(c.dateValue == dt){
14819                 ret = c;
14820                 return false;
14821             }
14822             return true;
14823         });
14824         
14825         return ret;
14826     },
14827     
14828     findCells : function(ev) {
14829         var s = ev.start.clone().clearTime().getTime();
14830        // Roo.log(s);
14831         var e= ev.end.clone().clearTime().getTime();
14832        // Roo.log(e);
14833         var ret = [];
14834         this.cells.each(function(c){
14835              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14836             
14837             if(c.dateValue > e){
14838                 return ;
14839             }
14840             if(c.dateValue < s){
14841                 return ;
14842             }
14843             ret.push(c);
14844         });
14845         
14846         return ret;    
14847     },
14848     
14849 //    findBestRow: function(cells)
14850 //    {
14851 //        var ret = 0;
14852 //        
14853 //        for (var i =0 ; i < cells.length;i++) {
14854 //            ret  = Math.max(cells[i].rows || 0,ret);
14855 //        }
14856 //        return ret;
14857 //        
14858 //    },
14859     
14860     
14861     addItem : function(ev)
14862     {
14863         // look for vertical location slot in
14864         var cells = this.findCells(ev);
14865         
14866 //        ev.row = this.findBestRow(cells);
14867         
14868         // work out the location.
14869         
14870         var crow = false;
14871         var rows = [];
14872         for(var i =0; i < cells.length; i++) {
14873             
14874             cells[i].row = cells[0].row;
14875             
14876             if(i == 0){
14877                 cells[i].row = cells[i].row + 1;
14878             }
14879             
14880             if (!crow) {
14881                 crow = {
14882                     start : cells[i],
14883                     end :  cells[i]
14884                 };
14885                 continue;
14886             }
14887             if (crow.start.getY() == cells[i].getY()) {
14888                 // on same row.
14889                 crow.end = cells[i];
14890                 continue;
14891             }
14892             // different row.
14893             rows.push(crow);
14894             crow = {
14895                 start: cells[i],
14896                 end : cells[i]
14897             };
14898             
14899         }
14900         
14901         rows.push(crow);
14902         ev.els = [];
14903         ev.rows = rows;
14904         ev.cells = cells;
14905         
14906         cells[0].events.push(ev);
14907         
14908         this.calevents.push(ev);
14909     },
14910     
14911     clearEvents: function() {
14912         
14913         if(!this.calevents){
14914             return;
14915         }
14916         
14917         Roo.each(this.cells.elements, function(c){
14918             c.row = 0;
14919             c.events = [];
14920             c.more = [];
14921         });
14922         
14923         Roo.each(this.calevents, function(e) {
14924             Roo.each(e.els, function(el) {
14925                 el.un('mouseenter' ,this.onEventEnter, this);
14926                 el.un('mouseleave' ,this.onEventLeave, this);
14927                 el.remove();
14928             },this);
14929         },this);
14930         
14931         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14932             e.remove();
14933         });
14934         
14935     },
14936     
14937     renderEvents: function()
14938     {   
14939         var _this = this;
14940         
14941         this.cells.each(function(c) {
14942             
14943             if(c.row < 5){
14944                 return;
14945             }
14946             
14947             var ev = c.events;
14948             
14949             var r = 4;
14950             if(c.row != c.events.length){
14951                 r = 4 - (4 - (c.row - c.events.length));
14952             }
14953             
14954             c.events = ev.slice(0, r);
14955             c.more = ev.slice(r);
14956             
14957             if(c.more.length && c.more.length == 1){
14958                 c.events.push(c.more.pop());
14959             }
14960             
14961             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14962             
14963         });
14964             
14965         this.cells.each(function(c) {
14966             
14967             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14968             
14969             
14970             for (var e = 0; e < c.events.length; e++){
14971                 var ev = c.events[e];
14972                 var rows = ev.rows;
14973                 
14974                 for(var i = 0; i < rows.length; i++) {
14975                 
14976                     // how many rows should it span..
14977
14978                     var  cfg = {
14979                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14980                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14981
14982                         unselectable : "on",
14983                         cn : [
14984                             {
14985                                 cls: 'fc-event-inner',
14986                                 cn : [
14987     //                                {
14988     //                                  tag:'span',
14989     //                                  cls: 'fc-event-time',
14990     //                                  html : cells.length > 1 ? '' : ev.time
14991     //                                },
14992                                     {
14993                                       tag:'span',
14994                                       cls: 'fc-event-title',
14995                                       html : String.format('{0}', ev.title)
14996                                     }
14997
14998
14999                                 ]
15000                             },
15001                             {
15002                                 cls: 'ui-resizable-handle ui-resizable-e',
15003                                 html : '&nbsp;&nbsp;&nbsp'
15004                             }
15005
15006                         ]
15007                     };
15008
15009                     if (i == 0) {
15010                         cfg.cls += ' fc-event-start';
15011                     }
15012                     if ((i+1) == rows.length) {
15013                         cfg.cls += ' fc-event-end';
15014                     }
15015
15016                     var ctr = _this.el.select('.fc-event-container',true).first();
15017                     var cg = ctr.createChild(cfg);
15018
15019                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15020                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15021
15022                     var r = (c.more.length) ? 1 : 0;
15023                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15024                     cg.setWidth(ebox.right - sbox.x -2);
15025
15026                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15027                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15028                     cg.on('click', _this.onEventClick, _this, ev);
15029
15030                     ev.els.push(cg);
15031                     
15032                 }
15033                 
15034             }
15035             
15036             
15037             if(c.more.length){
15038                 var  cfg = {
15039                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15040                     style : 'position: absolute',
15041                     unselectable : "on",
15042                     cn : [
15043                         {
15044                             cls: 'fc-event-inner',
15045                             cn : [
15046                                 {
15047                                   tag:'span',
15048                                   cls: 'fc-event-title',
15049                                   html : 'More'
15050                                 }
15051
15052
15053                             ]
15054                         },
15055                         {
15056                             cls: 'ui-resizable-handle ui-resizable-e',
15057                             html : '&nbsp;&nbsp;&nbsp'
15058                         }
15059
15060                     ]
15061                 };
15062
15063                 var ctr = _this.el.select('.fc-event-container',true).first();
15064                 var cg = ctr.createChild(cfg);
15065
15066                 var sbox = c.select('.fc-day-content',true).first().getBox();
15067                 var ebox = c.select('.fc-day-content',true).first().getBox();
15068                 //Roo.log(cg);
15069                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15070                 cg.setWidth(ebox.right - sbox.x -2);
15071
15072                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15073                 
15074             }
15075             
15076         });
15077         
15078         
15079         
15080     },
15081     
15082     onEventEnter: function (e, el,event,d) {
15083         this.fireEvent('evententer', this, el, event);
15084     },
15085     
15086     onEventLeave: function (e, el,event,d) {
15087         this.fireEvent('eventleave', this, el, event);
15088     },
15089     
15090     onEventClick: function (e, el,event,d) {
15091         this.fireEvent('eventclick', this, el, event);
15092     },
15093     
15094     onMonthChange: function () {
15095         this.store.load();
15096     },
15097     
15098     onMoreEventClick: function(e, el, more)
15099     {
15100         var _this = this;
15101         
15102         this.calpopover.placement = 'right';
15103         this.calpopover.setTitle('More');
15104         
15105         this.calpopover.setContent('');
15106         
15107         var ctr = this.calpopover.el.select('.popover-content', true).first();
15108         
15109         Roo.each(more, function(m){
15110             var cfg = {
15111                 cls : 'fc-event-hori fc-event-draggable',
15112                 html : m.title
15113             }
15114             var cg = ctr.createChild(cfg);
15115             
15116             cg.on('click', _this.onEventClick, _this, m);
15117         });
15118         
15119         this.calpopover.show(el);
15120         
15121         
15122     },
15123     
15124     onLoad: function () 
15125     {   
15126         this.calevents = [];
15127         var cal = this;
15128         
15129         if(this.store.getCount() > 0){
15130             this.store.data.each(function(d){
15131                cal.addItem({
15132                     id : d.data.id,
15133                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15134                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15135                     time : d.data.start_time,
15136                     title : d.data.title,
15137                     description : d.data.description,
15138                     venue : d.data.venue
15139                 });
15140             });
15141         }
15142         
15143         this.renderEvents();
15144         
15145         if(this.calevents.length && this.loadMask){
15146             this.maskEl.hide();
15147         }
15148     },
15149     
15150     onBeforeLoad: function()
15151     {
15152         this.clearEvents();
15153         if(this.loadMask){
15154             this.maskEl.show();
15155         }
15156     }
15157 });
15158
15159  
15160  /*
15161  * - LGPL
15162  *
15163  * element
15164  * 
15165  */
15166
15167 /**
15168  * @class Roo.bootstrap.Popover
15169  * @extends Roo.bootstrap.Component
15170  * Bootstrap Popover class
15171  * @cfg {String} html contents of the popover   (or false to use children..)
15172  * @cfg {String} title of popover (or false to hide)
15173  * @cfg {String} placement how it is placed
15174  * @cfg {String} trigger click || hover (or false to trigger manually)
15175  * @cfg {String} over what (parent or false to trigger manually.)
15176  * @cfg {Number} delay - delay before showing
15177  
15178  * @constructor
15179  * Create a new Popover
15180  * @param {Object} config The config object
15181  */
15182
15183 Roo.bootstrap.Popover = function(config){
15184     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15185 };
15186
15187 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15188     
15189     title: 'Fill in a title',
15190     html: false,
15191     
15192     placement : 'right',
15193     trigger : 'hover', // hover
15194     
15195     delay : 0,
15196     
15197     over: 'parent',
15198     
15199     can_build_overlaid : false,
15200     
15201     getChildContainer : function()
15202     {
15203         return this.el.select('.popover-content',true).first();
15204     },
15205     
15206     getAutoCreate : function(){
15207          Roo.log('make popover?');
15208         var cfg = {
15209            cls : 'popover roo-dynamic',
15210            style: 'display:block',
15211            cn : [
15212                 {
15213                     cls : 'arrow'
15214                 },
15215                 {
15216                     cls : 'popover-inner',
15217                     cn : [
15218                         {
15219                             tag: 'h3',
15220                             cls: 'popover-title',
15221                             html : this.title
15222                         },
15223                         {
15224                             cls : 'popover-content',
15225                             html : this.html
15226                         }
15227                     ]
15228                     
15229                 }
15230            ]
15231         };
15232         
15233         return cfg;
15234     },
15235     setTitle: function(str)
15236     {
15237         this.title = str;
15238         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15239     },
15240     setContent: function(str)
15241     {
15242         this.html = str;
15243         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15244     },
15245     // as it get's added to the bottom of the page.
15246     onRender : function(ct, position)
15247     {
15248         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15249         if(!this.el){
15250             var cfg = Roo.apply({},  this.getAutoCreate());
15251             cfg.id = Roo.id();
15252             
15253             if (this.cls) {
15254                 cfg.cls += ' ' + this.cls;
15255             }
15256             if (this.style) {
15257                 cfg.style = this.style;
15258             }
15259             Roo.log("adding to ")
15260             this.el = Roo.get(document.body).createChild(cfg, position);
15261             Roo.log(this.el);
15262         }
15263         this.initEvents();
15264     },
15265     
15266     initEvents : function()
15267     {
15268         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15269         this.el.enableDisplayMode('block');
15270         this.el.hide();
15271         if (this.over === false) {
15272             return; 
15273         }
15274         if (this.triggers === false) {
15275             return;
15276         }
15277         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15278         var triggers = this.trigger ? this.trigger.split(' ') : [];
15279         Roo.each(triggers, function(trigger) {
15280         
15281             if (trigger == 'click') {
15282                 on_el.on('click', this.toggle, this);
15283             } else if (trigger != 'manual') {
15284                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15285                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15286       
15287                 on_el.on(eventIn  ,this.enter, this);
15288                 on_el.on(eventOut, this.leave, this);
15289             }
15290         }, this);
15291         
15292     },
15293     
15294     
15295     // private
15296     timeout : null,
15297     hoverState : null,
15298     
15299     toggle : function () {
15300         this.hoverState == 'in' ? this.leave() : this.enter();
15301     },
15302     
15303     enter : function () {
15304        
15305     
15306         clearTimeout(this.timeout);
15307     
15308         this.hoverState = 'in';
15309     
15310         if (!this.delay || !this.delay.show) {
15311             this.show();
15312             return;
15313         }
15314         var _t = this;
15315         this.timeout = setTimeout(function () {
15316             if (_t.hoverState == 'in') {
15317                 _t.show();
15318             }
15319         }, this.delay.show)
15320     },
15321     leave : function() {
15322         clearTimeout(this.timeout);
15323     
15324         this.hoverState = 'out';
15325     
15326         if (!this.delay || !this.delay.hide) {
15327             this.hide();
15328             return;
15329         }
15330         var _t = this;
15331         this.timeout = setTimeout(function () {
15332             if (_t.hoverState == 'out') {
15333                 _t.hide();
15334             }
15335         }, this.delay.hide)
15336     },
15337     
15338     show : function (on_el)
15339     {
15340         if (!on_el) {
15341             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15342         }
15343         // set content.
15344         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15345         if (this.html !== false) {
15346             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15347         }
15348         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15349         if (!this.title.length) {
15350             this.el.select('.popover-title',true).hide();
15351         }
15352         
15353         var placement = typeof this.placement == 'function' ?
15354             this.placement.call(this, this.el, on_el) :
15355             this.placement;
15356             
15357         var autoToken = /\s?auto?\s?/i;
15358         var autoPlace = autoToken.test(placement);
15359         if (autoPlace) {
15360             placement = placement.replace(autoToken, '') || 'top';
15361         }
15362         
15363         //this.el.detach()
15364         //this.el.setXY([0,0]);
15365         this.el.show();
15366         this.el.dom.style.display='block';
15367         this.el.addClass(placement);
15368         
15369         //this.el.appendTo(on_el);
15370         
15371         var p = this.getPosition();
15372         var box = this.el.getBox();
15373         
15374         if (autoPlace) {
15375             // fixme..
15376         }
15377         var align = Roo.bootstrap.Popover.alignment[placement];
15378         this.el.alignTo(on_el, align[0],align[1]);
15379         //var arrow = this.el.select('.arrow',true).first();
15380         //arrow.set(align[2], 
15381         
15382         this.el.addClass('in');
15383         
15384         
15385         if (this.el.hasClass('fade')) {
15386             // fade it?
15387         }
15388         
15389     },
15390     hide : function()
15391     {
15392         this.el.setXY([0,0]);
15393         this.el.removeClass('in');
15394         this.el.hide();
15395         this.hoverState = null;
15396         
15397     }
15398     
15399 });
15400
15401 Roo.bootstrap.Popover.alignment = {
15402     'left' : ['r-l', [-10,0], 'right'],
15403     'right' : ['l-r', [10,0], 'left'],
15404     'bottom' : ['t-b', [0,10], 'top'],
15405     'top' : [ 'b-t', [0,-10], 'bottom']
15406 };
15407
15408  /*
15409  * - LGPL
15410  *
15411  * Progress
15412  * 
15413  */
15414
15415 /**
15416  * @class Roo.bootstrap.Progress
15417  * @extends Roo.bootstrap.Component
15418  * Bootstrap Progress class
15419  * @cfg {Boolean} striped striped of the progress bar
15420  * @cfg {Boolean} active animated of the progress bar
15421  * 
15422  * 
15423  * @constructor
15424  * Create a new Progress
15425  * @param {Object} config The config object
15426  */
15427
15428 Roo.bootstrap.Progress = function(config){
15429     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15430 };
15431
15432 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15433     
15434     striped : false,
15435     active: false,
15436     
15437     getAutoCreate : function(){
15438         var cfg = {
15439             tag: 'div',
15440             cls: 'progress'
15441         };
15442         
15443         
15444         if(this.striped){
15445             cfg.cls += ' progress-striped';
15446         }
15447       
15448         if(this.active){
15449             cfg.cls += ' active';
15450         }
15451         
15452         
15453         return cfg;
15454     }
15455    
15456 });
15457
15458  
15459
15460  /*
15461  * - LGPL
15462  *
15463  * ProgressBar
15464  * 
15465  */
15466
15467 /**
15468  * @class Roo.bootstrap.ProgressBar
15469  * @extends Roo.bootstrap.Component
15470  * Bootstrap ProgressBar class
15471  * @cfg {Number} aria_valuenow aria-value now
15472  * @cfg {Number} aria_valuemin aria-value min
15473  * @cfg {Number} aria_valuemax aria-value max
15474  * @cfg {String} label label for the progress bar
15475  * @cfg {String} panel (success | info | warning | danger )
15476  * @cfg {String} role role of the progress bar
15477  * @cfg {String} sr_only text
15478  * 
15479  * 
15480  * @constructor
15481  * Create a new ProgressBar
15482  * @param {Object} config The config object
15483  */
15484
15485 Roo.bootstrap.ProgressBar = function(config){
15486     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15487 };
15488
15489 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15490     
15491     aria_valuenow : 0,
15492     aria_valuemin : 0,
15493     aria_valuemax : 100,
15494     label : false,
15495     panel : false,
15496     role : false,
15497     sr_only: false,
15498     
15499     getAutoCreate : function()
15500     {
15501         
15502         var cfg = {
15503             tag: 'div',
15504             cls: 'progress-bar',
15505             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15506         };
15507         
15508         if(this.sr_only){
15509             cfg.cn = {
15510                 tag: 'span',
15511                 cls: 'sr-only',
15512                 html: this.sr_only
15513             }
15514         }
15515         
15516         if(this.role){
15517             cfg.role = this.role;
15518         }
15519         
15520         if(this.aria_valuenow){
15521             cfg['aria-valuenow'] = this.aria_valuenow;
15522         }
15523         
15524         if(this.aria_valuemin){
15525             cfg['aria-valuemin'] = this.aria_valuemin;
15526         }
15527         
15528         if(this.aria_valuemax){
15529             cfg['aria-valuemax'] = this.aria_valuemax;
15530         }
15531         
15532         if(this.label && !this.sr_only){
15533             cfg.html = this.label;
15534         }
15535         
15536         if(this.panel){
15537             cfg.cls += ' progress-bar-' + this.panel;
15538         }
15539         
15540         return cfg;
15541     },
15542     
15543     update : function(aria_valuenow)
15544     {
15545         this.aria_valuenow = aria_valuenow;
15546         
15547         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15548     }
15549    
15550 });
15551
15552  
15553
15554  /*
15555  * - LGPL
15556  *
15557  * column
15558  * 
15559  */
15560
15561 /**
15562  * @class Roo.bootstrap.TabGroup
15563  * @extends Roo.bootstrap.Column
15564  * Bootstrap Column class
15565  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15566  * @cfg {Boolean} carousel true to make the group behave like a carousel
15567  * @cfg {Number} bullets show the panel pointer.. default 0
15568  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15569  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15570  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15571  * 
15572  * @constructor
15573  * Create a new TabGroup
15574  * @param {Object} config The config object
15575  */
15576
15577 Roo.bootstrap.TabGroup = function(config){
15578     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15579     if (!this.navId) {
15580         this.navId = Roo.id();
15581     }
15582     this.tabs = [];
15583     Roo.bootstrap.TabGroup.register(this);
15584     
15585 };
15586
15587 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15588     
15589     carousel : false,
15590     transition : false,
15591     bullets : 0,
15592     timer : 0,
15593     autoslide : false,
15594     slideFn : false,
15595     slideOnTouch : false,
15596     
15597     getAutoCreate : function()
15598     {
15599         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15600         
15601         cfg.cls += ' tab-content';
15602         
15603         Roo.log('get auto create...............');
15604         
15605         if (this.carousel) {
15606             cfg.cls += ' carousel slide';
15607             
15608             cfg.cn = [{
15609                cls : 'carousel-inner'
15610             }];
15611         
15612             if(this.bullets > 0 && !Roo.isTouch){
15613                 
15614                 var bullets = {
15615                     cls : 'carousel-bullets',
15616                     cn : []
15617                 };
15618                 
15619                 if(this.bullets_cls){
15620                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15621                 }
15622                 
15623                 for (var i = 0; i < this.bullets; i++){
15624                     bullets.cn.push({
15625                         cls : 'bullet bullet-' + i
15626                     });
15627                 }
15628                 
15629                 bullets.cn.push({
15630                     cls : 'clear'
15631                 });
15632                 
15633                 cfg.cn[0].cn = bullets;
15634             }
15635         }
15636         
15637         return cfg;
15638     },
15639     
15640     initEvents:  function()
15641     {
15642         Roo.log('-------- init events on tab group ---------');
15643         
15644         if(this.bullets > 0 && !Roo.isTouch){
15645             this.initBullet();
15646         }
15647         
15648         Roo.log(this);
15649         
15650         if(Roo.isTouch && this.slideOnTouch){
15651             this.el.on("touchstart", this.onTouchStart, this);
15652         }
15653         
15654         if(this.autoslide){
15655             var _this = this;
15656             
15657             this.slideFn = window.setInterval(function() {
15658                 _this.showPanelNext();
15659             }, this.timer);
15660         }
15661         
15662     },
15663     
15664     onTouchStart : function(e, el, o)
15665     {
15666         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15667             return;
15668         }
15669         
15670         this.showPanelNext();
15671     },
15672     
15673     getChildContainer : function()
15674     {
15675         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15676     },
15677     
15678     /**
15679     * register a Navigation item
15680     * @param {Roo.bootstrap.NavItem} the navitem to add
15681     */
15682     register : function(item)
15683     {
15684         this.tabs.push( item);
15685         item.navId = this.navId; // not really needed..
15686     
15687     },
15688     
15689     getActivePanel : function()
15690     {
15691         var r = false;
15692         Roo.each(this.tabs, function(t) {
15693             if (t.active) {
15694                 r = t;
15695                 return false;
15696             }
15697             return null;
15698         });
15699         return r;
15700         
15701     },
15702     getPanelByName : function(n)
15703     {
15704         var r = false;
15705         Roo.each(this.tabs, function(t) {
15706             if (t.tabId == n) {
15707                 r = t;
15708                 return false;
15709             }
15710             return null;
15711         });
15712         return r;
15713     },
15714     indexOfPanel : function(p)
15715     {
15716         var r = false;
15717         Roo.each(this.tabs, function(t,i) {
15718             if (t.tabId == p.tabId) {
15719                 r = i;
15720                 return false;
15721             }
15722             return null;
15723         });
15724         return r;
15725     },
15726     /**
15727      * show a specific panel
15728      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15729      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15730      */
15731     showPanel : function (pan)
15732     {
15733         if(this.transition){
15734             Roo.log("waiting for the transitionend");
15735             return;
15736         }
15737         
15738         if (typeof(pan) == 'number') {
15739             pan = this.tabs[pan];
15740         }
15741         if (typeof(pan) == 'string') {
15742             pan = this.getPanelByName(pan);
15743         }
15744         if (pan.tabId == this.getActivePanel().tabId) {
15745             return true;
15746         }
15747         var cur = this.getActivePanel();
15748         
15749         if (false === cur.fireEvent('beforedeactivate')) {
15750             return false;
15751         }
15752         
15753         if(this.bullets > 0 && !Roo.isTouch){
15754             this.setActiveBullet(this.indexOfPanel(pan));
15755         }
15756         
15757         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15758             
15759             this.transition = true;
15760             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15761             var lr = dir == 'next' ? 'left' : 'right';
15762             pan.el.addClass(dir); // or prev
15763             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15764             cur.el.addClass(lr); // or right
15765             pan.el.addClass(lr);
15766             
15767             var _this = this;
15768             cur.el.on('transitionend', function() {
15769                 Roo.log("trans end?");
15770                 
15771                 pan.el.removeClass([lr,dir]);
15772                 pan.setActive(true);
15773                 
15774                 cur.el.removeClass([lr]);
15775                 cur.setActive(false);
15776                 
15777                 _this.transition = false;
15778                 
15779             }, this, { single:  true } );
15780             
15781             return true;
15782         }
15783         
15784         cur.setActive(false);
15785         pan.setActive(true);
15786         
15787         return true;
15788         
15789     },
15790     showPanelNext : function()
15791     {
15792         var i = this.indexOfPanel(this.getActivePanel());
15793         
15794         if (i >= this.tabs.length - 1 && !this.autoslide) {
15795             return;
15796         }
15797         
15798         if (i >= this.tabs.length - 1 && this.autoslide) {
15799             i = -1;
15800         }
15801         
15802         this.showPanel(this.tabs[i+1]);
15803     },
15804     
15805     showPanelPrev : function()
15806     {
15807         var i = this.indexOfPanel(this.getActivePanel());
15808         
15809         if (i  < 1 && !this.autoslide) {
15810             return;
15811         }
15812         
15813         if (i < 1 && this.autoslide) {
15814             i = this.tabs.length;
15815         }
15816         
15817         this.showPanel(this.tabs[i-1]);
15818     },
15819     
15820     initBullet : function()
15821     {
15822         if(Roo.isTouch){
15823             return;
15824         }
15825         
15826         var _this = this;
15827         
15828         for (var i = 0; i < this.bullets; i++){
15829             var bullet = this.el.select('.bullet-' + i, true).first();
15830
15831             if(!bullet){
15832                 continue;
15833             }
15834
15835             bullet.on('click', (function(e, el, o, ii, t){
15836
15837                 e.preventDefault();
15838
15839                 _this.showPanel(ii);
15840
15841                 if(_this.autoslide && _this.slideFn){
15842                     clearInterval(_this.slideFn);
15843                     _this.slideFn = window.setInterval(function() {
15844                         _this.showPanelNext();
15845                     }, _this.timer);
15846                 }
15847
15848             }).createDelegate(this, [i, bullet], true));
15849         }
15850     },
15851     
15852     setActiveBullet : function(i)
15853     {
15854         if(Roo.isTouch){
15855             return;
15856         }
15857         
15858         Roo.each(this.el.select('.bullet', true).elements, function(el){
15859             el.removeClass('selected');
15860         });
15861
15862         var bullet = this.el.select('.bullet-' + i, true).first();
15863         
15864         if(!bullet){
15865             return;
15866         }
15867         
15868         bullet.addClass('selected');
15869     }
15870     
15871     
15872   
15873 });
15874
15875  
15876
15877  
15878  
15879 Roo.apply(Roo.bootstrap.TabGroup, {
15880     
15881     groups: {},
15882      /**
15883     * register a Navigation Group
15884     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15885     */
15886     register : function(navgrp)
15887     {
15888         this.groups[navgrp.navId] = navgrp;
15889         
15890     },
15891     /**
15892     * fetch a Navigation Group based on the navigation ID
15893     * if one does not exist , it will get created.
15894     * @param {string} the navgroup to add
15895     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15896     */
15897     get: function(navId) {
15898         if (typeof(this.groups[navId]) == 'undefined') {
15899             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15900         }
15901         return this.groups[navId] ;
15902     }
15903     
15904     
15905     
15906 });
15907
15908  /*
15909  * - LGPL
15910  *
15911  * TabPanel
15912  * 
15913  */
15914
15915 /**
15916  * @class Roo.bootstrap.TabPanel
15917  * @extends Roo.bootstrap.Component
15918  * Bootstrap TabPanel class
15919  * @cfg {Boolean} active panel active
15920  * @cfg {String} html panel content
15921  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15922  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15923  * 
15924  * 
15925  * @constructor
15926  * Create a new TabPanel
15927  * @param {Object} config The config object
15928  */
15929
15930 Roo.bootstrap.TabPanel = function(config){
15931     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15932     this.addEvents({
15933         /**
15934              * @event changed
15935              * Fires when the active status changes
15936              * @param {Roo.bootstrap.TabPanel} this
15937              * @param {Boolean} state the new state
15938             
15939          */
15940         'changed': true,
15941         /**
15942              * @event beforedeactivate
15943              * Fires before a tab is de-activated - can be used to do validation on a form.
15944              * @param {Roo.bootstrap.TabPanel} this
15945              * @return {Boolean} false if there is an error
15946             
15947          */
15948         'beforedeactivate': true
15949      });
15950     
15951     this.tabId = this.tabId || Roo.id();
15952   
15953 };
15954
15955 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15956     
15957     active: false,
15958     html: false,
15959     tabId: false,
15960     navId : false,
15961     
15962     getAutoCreate : function(){
15963         var cfg = {
15964             tag: 'div',
15965             // item is needed for carousel - not sure if it has any effect otherwise
15966             cls: 'tab-pane item',
15967             html: this.html || ''
15968         };
15969         
15970         if(this.active){
15971             cfg.cls += ' active';
15972         }
15973         
15974         if(this.tabId){
15975             cfg.tabId = this.tabId;
15976         }
15977         
15978         
15979         return cfg;
15980     },
15981     
15982     initEvents:  function()
15983     {
15984         Roo.log('-------- init events on tab panel ---------');
15985         
15986         var p = this.parent();
15987         this.navId = this.navId || p.navId;
15988         
15989         if (typeof(this.navId) != 'undefined') {
15990             // not really needed.. but just in case.. parent should be a NavGroup.
15991             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15992             Roo.log(['register', tg, this]);
15993             tg.register(this);
15994             
15995             var i = tg.tabs.length - 1;
15996             
15997             if(this.active && tg.bullets > 0 && i < tg.bullets){
15998                 tg.setActiveBullet(i);
15999             }
16000         }
16001         
16002     },
16003     
16004     
16005     onRender : function(ct, position)
16006     {
16007        // Roo.log("Call onRender: " + this.xtype);
16008         
16009         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16010         
16011         
16012         
16013         
16014         
16015     },
16016     
16017     setActive: function(state)
16018     {
16019         Roo.log("panel - set active " + this.tabId + "=" + state);
16020         
16021         this.active = state;
16022         if (!state) {
16023             this.el.removeClass('active');
16024             
16025         } else  if (!this.el.hasClass('active')) {
16026             this.el.addClass('active');
16027         }
16028         
16029         this.fireEvent('changed', this, state);
16030     }
16031     
16032     
16033 });
16034  
16035
16036  
16037
16038  /*
16039  * - LGPL
16040  *
16041  * DateField
16042  * 
16043  */
16044
16045 /**
16046  * @class Roo.bootstrap.DateField
16047  * @extends Roo.bootstrap.Input
16048  * Bootstrap DateField class
16049  * @cfg {Number} weekStart default 0
16050  * @cfg {String} viewMode default empty, (months|years)
16051  * @cfg {String} minViewMode default empty, (months|years)
16052  * @cfg {Number} startDate default -Infinity
16053  * @cfg {Number} endDate default Infinity
16054  * @cfg {Boolean} todayHighlight default false
16055  * @cfg {Boolean} todayBtn default false
16056  * @cfg {Boolean} calendarWeeks default false
16057  * @cfg {Object} daysOfWeekDisabled default empty
16058  * @cfg {Boolean} singleMode default false (true | false)
16059  * 
16060  * @cfg {Boolean} keyboardNavigation default true
16061  * @cfg {String} language default en
16062  * 
16063  * @constructor
16064  * Create a new DateField
16065  * @param {Object} config The config object
16066  */
16067
16068 Roo.bootstrap.DateField = function(config){
16069     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16070      this.addEvents({
16071             /**
16072              * @event show
16073              * Fires when this field show.
16074              * @param {Roo.bootstrap.DateField} this
16075              * @param {Mixed} date The date value
16076              */
16077             show : true,
16078             /**
16079              * @event show
16080              * Fires when this field hide.
16081              * @param {Roo.bootstrap.DateField} this
16082              * @param {Mixed} date The date value
16083              */
16084             hide : true,
16085             /**
16086              * @event select
16087              * Fires when select a date.
16088              * @param {Roo.bootstrap.DateField} this
16089              * @param {Mixed} date The date value
16090              */
16091             select : true
16092         });
16093 };
16094
16095 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16096     
16097     /**
16098      * @cfg {String} format
16099      * The default date format string which can be overriden for localization support.  The format must be
16100      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16101      */
16102     format : "m/d/y",
16103     /**
16104      * @cfg {String} altFormats
16105      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16106      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16107      */
16108     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16109     
16110     weekStart : 0,
16111     
16112     viewMode : '',
16113     
16114     minViewMode : '',
16115     
16116     todayHighlight : false,
16117     
16118     todayBtn: false,
16119     
16120     language: 'en',
16121     
16122     keyboardNavigation: true,
16123     
16124     calendarWeeks: false,
16125     
16126     startDate: -Infinity,
16127     
16128     endDate: Infinity,
16129     
16130     daysOfWeekDisabled: [],
16131     
16132     _events: [],
16133     
16134     singleMode : false,
16135     
16136     UTCDate: function()
16137     {
16138         return new Date(Date.UTC.apply(Date, arguments));
16139     },
16140     
16141     UTCToday: function()
16142     {
16143         var today = new Date();
16144         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16145     },
16146     
16147     getDate: function() {
16148             var d = this.getUTCDate();
16149             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16150     },
16151     
16152     getUTCDate: function() {
16153             return this.date;
16154     },
16155     
16156     setDate: function(d) {
16157             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16158     },
16159     
16160     setUTCDate: function(d) {
16161             this.date = d;
16162             this.setValue(this.formatDate(this.date));
16163     },
16164         
16165     onRender: function(ct, position)
16166     {
16167         
16168         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16169         
16170         this.language = this.language || 'en';
16171         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16172         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16173         
16174         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16175         this.format = this.format || 'm/d/y';
16176         this.isInline = false;
16177         this.isInput = true;
16178         this.component = this.el.select('.add-on', true).first() || false;
16179         this.component = (this.component && this.component.length === 0) ? false : this.component;
16180         this.hasInput = this.component && this.inputEL().length;
16181         
16182         if (typeof(this.minViewMode === 'string')) {
16183             switch (this.minViewMode) {
16184                 case 'months':
16185                     this.minViewMode = 1;
16186                     break;
16187                 case 'years':
16188                     this.minViewMode = 2;
16189                     break;
16190                 default:
16191                     this.minViewMode = 0;
16192                     break;
16193             }
16194         }
16195         
16196         if (typeof(this.viewMode === 'string')) {
16197             switch (this.viewMode) {
16198                 case 'months':
16199                     this.viewMode = 1;
16200                     break;
16201                 case 'years':
16202                     this.viewMode = 2;
16203                     break;
16204                 default:
16205                     this.viewMode = 0;
16206                     break;
16207             }
16208         }
16209                 
16210         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16211         
16212 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16213         
16214         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16215         
16216         this.picker().on('mousedown', this.onMousedown, this);
16217         this.picker().on('click', this.onClick, this);
16218         
16219         this.picker().addClass('datepicker-dropdown');
16220         
16221         this.startViewMode = this.viewMode;
16222         
16223         if(this.singleMode){
16224             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16225                 v.setVisibilityMode(Roo.Element.DISPLAY)
16226                 v.hide();
16227             });
16228             
16229             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16230                 v.setStyle('width', '189px');
16231             });
16232         }
16233         
16234         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16235             if(!this.calendarWeeks){
16236                 v.remove();
16237                 return;
16238             }
16239             
16240             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16241             v.attr('colspan', function(i, val){
16242                 return parseInt(val) + 1;
16243             });
16244         })
16245                         
16246         
16247         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16248         
16249         this.setStartDate(this.startDate);
16250         this.setEndDate(this.endDate);
16251         
16252         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16253         
16254         this.fillDow();
16255         this.fillMonths();
16256         this.update();
16257         this.showMode();
16258         
16259         if(this.isInline) {
16260             this.show();
16261         }
16262     },
16263     
16264     picker : function()
16265     {
16266         return this.pickerEl;
16267 //        return this.el.select('.datepicker', true).first();
16268     },
16269     
16270     fillDow: function()
16271     {
16272         var dowCnt = this.weekStart;
16273         
16274         var dow = {
16275             tag: 'tr',
16276             cn: [
16277                 
16278             ]
16279         };
16280         
16281         if(this.calendarWeeks){
16282             dow.cn.push({
16283                 tag: 'th',
16284                 cls: 'cw',
16285                 html: '&nbsp;'
16286             })
16287         }
16288         
16289         while (dowCnt < this.weekStart + 7) {
16290             dow.cn.push({
16291                 tag: 'th',
16292                 cls: 'dow',
16293                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16294             });
16295         }
16296         
16297         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16298     },
16299     
16300     fillMonths: function()
16301     {    
16302         var i = 0;
16303         var months = this.picker().select('>.datepicker-months td', true).first();
16304         
16305         months.dom.innerHTML = '';
16306         
16307         while (i < 12) {
16308             var month = {
16309                 tag: 'span',
16310                 cls: 'month',
16311                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16312             }
16313             
16314             months.createChild(month);
16315         }
16316         
16317     },
16318     
16319     update: function()
16320     {
16321         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;
16322         
16323         if (this.date < this.startDate) {
16324             this.viewDate = new Date(this.startDate);
16325         } else if (this.date > this.endDate) {
16326             this.viewDate = new Date(this.endDate);
16327         } else {
16328             this.viewDate = new Date(this.date);
16329         }
16330         
16331         this.fill();
16332     },
16333     
16334     fill: function() 
16335     {
16336         var d = new Date(this.viewDate),
16337                 year = d.getUTCFullYear(),
16338                 month = d.getUTCMonth(),
16339                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16340                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16341                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16342                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16343                 currentDate = this.date && this.date.valueOf(),
16344                 today = this.UTCToday();
16345         
16346         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16347         
16348 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16349         
16350 //        this.picker.select('>tfoot th.today').
16351 //                                              .text(dates[this.language].today)
16352 //                                              .toggle(this.todayBtn !== false);
16353     
16354         this.updateNavArrows();
16355         this.fillMonths();
16356                                                 
16357         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16358         
16359         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16360          
16361         prevMonth.setUTCDate(day);
16362         
16363         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16364         
16365         var nextMonth = new Date(prevMonth);
16366         
16367         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16368         
16369         nextMonth = nextMonth.valueOf();
16370         
16371         var fillMonths = false;
16372         
16373         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16374         
16375         while(prevMonth.valueOf() < nextMonth) {
16376             var clsName = '';
16377             
16378             if (prevMonth.getUTCDay() === this.weekStart) {
16379                 if(fillMonths){
16380                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16381                 }
16382                     
16383                 fillMonths = {
16384                     tag: 'tr',
16385                     cn: []
16386                 };
16387                 
16388                 if(this.calendarWeeks){
16389                     // ISO 8601: First week contains first thursday.
16390                     // ISO also states week starts on Monday, but we can be more abstract here.
16391                     var
16392                     // Start of current week: based on weekstart/current date
16393                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16394                     // Thursday of this week
16395                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16396                     // First Thursday of year, year from thursday
16397                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16398                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16399                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16400                     
16401                     fillMonths.cn.push({
16402                         tag: 'td',
16403                         cls: 'cw',
16404                         html: calWeek
16405                     });
16406                 }
16407             }
16408             
16409             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16410                 clsName += ' old';
16411             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16412                 clsName += ' new';
16413             }
16414             if (this.todayHighlight &&
16415                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16416                 prevMonth.getUTCMonth() == today.getMonth() &&
16417                 prevMonth.getUTCDate() == today.getDate()) {
16418                 clsName += ' today';
16419             }
16420             
16421             if (currentDate && prevMonth.valueOf() === currentDate) {
16422                 clsName += ' active';
16423             }
16424             
16425             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16426                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16427                     clsName += ' disabled';
16428             }
16429             
16430             fillMonths.cn.push({
16431                 tag: 'td',
16432                 cls: 'day ' + clsName,
16433                 html: prevMonth.getDate()
16434             })
16435             
16436             prevMonth.setDate(prevMonth.getDate()+1);
16437         }
16438           
16439         var currentYear = this.date && this.date.getUTCFullYear();
16440         var currentMonth = this.date && this.date.getUTCMonth();
16441         
16442         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16443         
16444         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16445             v.removeClass('active');
16446             
16447             if(currentYear === year && k === currentMonth){
16448                 v.addClass('active');
16449             }
16450             
16451             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16452                 v.addClass('disabled');
16453             }
16454             
16455         });
16456         
16457         
16458         year = parseInt(year/10, 10) * 10;
16459         
16460         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16461         
16462         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16463         
16464         year -= 1;
16465         for (var i = -1; i < 11; i++) {
16466             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16467                 tag: 'span',
16468                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16469                 html: year
16470             })
16471             
16472             year += 1;
16473         }
16474     },
16475     
16476     showMode: function(dir) 
16477     {
16478         if (dir) {
16479             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16480         }
16481         
16482         Roo.each(this.picker().select('>div',true).elements, function(v){
16483             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16484             v.hide();
16485         });
16486         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16487     },
16488     
16489     place: function()
16490     {
16491         if(this.isInline) return;
16492         
16493         this.picker().removeClass(['bottom', 'top']);
16494         
16495         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16496             /*
16497              * place to the top of element!
16498              *
16499              */
16500             
16501             this.picker().addClass('top');
16502             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16503             
16504             return;
16505         }
16506         
16507         this.picker().addClass('bottom');
16508         
16509         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16510     },
16511     
16512     parseDate : function(value)
16513     {
16514         if(!value || value instanceof Date){
16515             return value;
16516         }
16517         var v = Date.parseDate(value, this.format);
16518         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16519             v = Date.parseDate(value, 'Y-m-d');
16520         }
16521         if(!v && this.altFormats){
16522             if(!this.altFormatsArray){
16523                 this.altFormatsArray = this.altFormats.split("|");
16524             }
16525             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16526                 v = Date.parseDate(value, this.altFormatsArray[i]);
16527             }
16528         }
16529         return v;
16530     },
16531     
16532     formatDate : function(date, fmt)
16533     {   
16534         return (!date || !(date instanceof Date)) ?
16535         date : date.dateFormat(fmt || this.format);
16536     },
16537     
16538     onFocus : function()
16539     {
16540         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16541         this.show();
16542     },
16543     
16544     onBlur : function()
16545     {
16546         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16547         
16548         var d = this.inputEl().getValue();
16549         
16550         this.setValue(d);
16551                 
16552         this.hide();
16553     },
16554     
16555     show : function()
16556     {
16557         this.picker().show();
16558         this.update();
16559         this.place();
16560         
16561         this.fireEvent('show', this, this.date);
16562     },
16563     
16564     hide : function()
16565     {
16566         if(this.isInline) return;
16567         this.picker().hide();
16568         this.viewMode = this.startViewMode;
16569         this.showMode();
16570         
16571         this.fireEvent('hide', this, this.date);
16572         
16573     },
16574     
16575     onMousedown: function(e)
16576     {
16577         e.stopPropagation();
16578         e.preventDefault();
16579     },
16580     
16581     keyup: function(e)
16582     {
16583         Roo.bootstrap.DateField.superclass.keyup.call(this);
16584         this.update();
16585     },
16586
16587     setValue: function(v)
16588     {
16589         
16590         // v can be a string or a date..
16591         
16592         
16593         var d = new Date(this.parseDate(v) ).clearTime();
16594         
16595         if(isNaN(d.getTime())){
16596             this.date = this.viewDate = '';
16597             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16598             return;
16599         }
16600         
16601         v = this.formatDate(d);
16602         
16603         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16604         
16605         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16606      
16607         this.update();
16608
16609         this.fireEvent('select', this, this.date);
16610         
16611     },
16612     
16613     getValue: function()
16614     {
16615         return this.formatDate(this.date);
16616     },
16617     
16618     fireKey: function(e)
16619     {
16620         if (!this.picker().isVisible()){
16621             if (e.keyCode == 27) // allow escape to hide and re-show picker
16622                 this.show();
16623             return;
16624         }
16625         
16626         var dateChanged = false,
16627         dir, day, month,
16628         newDate, newViewDate;
16629         
16630         switch(e.keyCode){
16631             case 27: // escape
16632                 this.hide();
16633                 e.preventDefault();
16634                 break;
16635             case 37: // left
16636             case 39: // right
16637                 if (!this.keyboardNavigation) break;
16638                 dir = e.keyCode == 37 ? -1 : 1;
16639                 
16640                 if (e.ctrlKey){
16641                     newDate = this.moveYear(this.date, dir);
16642                     newViewDate = this.moveYear(this.viewDate, dir);
16643                 } else if (e.shiftKey){
16644                     newDate = this.moveMonth(this.date, dir);
16645                     newViewDate = this.moveMonth(this.viewDate, dir);
16646                 } else {
16647                     newDate = new Date(this.date);
16648                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16649                     newViewDate = new Date(this.viewDate);
16650                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16651                 }
16652                 if (this.dateWithinRange(newDate)){
16653                     this.date = newDate;
16654                     this.viewDate = newViewDate;
16655                     this.setValue(this.formatDate(this.date));
16656 //                    this.update();
16657                     e.preventDefault();
16658                     dateChanged = true;
16659                 }
16660                 break;
16661             case 38: // up
16662             case 40: // down
16663                 if (!this.keyboardNavigation) break;
16664                 dir = e.keyCode == 38 ? -1 : 1;
16665                 if (e.ctrlKey){
16666                     newDate = this.moveYear(this.date, dir);
16667                     newViewDate = this.moveYear(this.viewDate, dir);
16668                 } else if (e.shiftKey){
16669                     newDate = this.moveMonth(this.date, dir);
16670                     newViewDate = this.moveMonth(this.viewDate, dir);
16671                 } else {
16672                     newDate = new Date(this.date);
16673                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16674                     newViewDate = new Date(this.viewDate);
16675                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16676                 }
16677                 if (this.dateWithinRange(newDate)){
16678                     this.date = newDate;
16679                     this.viewDate = newViewDate;
16680                     this.setValue(this.formatDate(this.date));
16681 //                    this.update();
16682                     e.preventDefault();
16683                     dateChanged = true;
16684                 }
16685                 break;
16686             case 13: // enter
16687                 this.setValue(this.formatDate(this.date));
16688                 this.hide();
16689                 e.preventDefault();
16690                 break;
16691             case 9: // tab
16692                 this.setValue(this.formatDate(this.date));
16693                 this.hide();
16694                 break;
16695             case 16: // shift
16696             case 17: // ctrl
16697             case 18: // alt
16698                 break;
16699             default :
16700                 this.hide();
16701                 
16702         }
16703     },
16704     
16705     
16706     onClick: function(e) 
16707     {
16708         e.stopPropagation();
16709         e.preventDefault();
16710         
16711         var target = e.getTarget();
16712         
16713         if(target.nodeName.toLowerCase() === 'i'){
16714             target = Roo.get(target).dom.parentNode;
16715         }
16716         
16717         var nodeName = target.nodeName;
16718         var className = target.className;
16719         var html = target.innerHTML;
16720         //Roo.log(nodeName);
16721         
16722         switch(nodeName.toLowerCase()) {
16723             case 'th':
16724                 switch(className) {
16725                     case 'switch':
16726                         this.showMode(1);
16727                         break;
16728                     case 'prev':
16729                     case 'next':
16730                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16731                         switch(this.viewMode){
16732                                 case 0:
16733                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16734                                         break;
16735                                 case 1:
16736                                 case 2:
16737                                         this.viewDate = this.moveYear(this.viewDate, dir);
16738                                         break;
16739                         }
16740                         this.fill();
16741                         break;
16742                     case 'today':
16743                         var date = new Date();
16744                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16745 //                        this.fill()
16746                         this.setValue(this.formatDate(this.date));
16747                         
16748                         this.hide();
16749                         break;
16750                 }
16751                 break;
16752             case 'span':
16753                 if (className.indexOf('disabled') < 0) {
16754                     this.viewDate.setUTCDate(1);
16755                     if (className.indexOf('month') > -1) {
16756                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16757                     } else {
16758                         var year = parseInt(html, 10) || 0;
16759                         this.viewDate.setUTCFullYear(year);
16760                         
16761                     }
16762                     
16763                     if(this.singleMode){
16764                         this.setValue(this.formatDate(this.viewDate));
16765                         this.hide();
16766                         return;
16767                     }
16768                     
16769                     this.showMode(-1);
16770                     this.fill();
16771                 }
16772                 break;
16773                 
16774             case 'td':
16775                 //Roo.log(className);
16776                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16777                     var day = parseInt(html, 10) || 1;
16778                     var year = this.viewDate.getUTCFullYear(),
16779                         month = this.viewDate.getUTCMonth();
16780
16781                     if (className.indexOf('old') > -1) {
16782                         if(month === 0 ){
16783                             month = 11;
16784                             year -= 1;
16785                         }else{
16786                             month -= 1;
16787                         }
16788                     } else if (className.indexOf('new') > -1) {
16789                         if (month == 11) {
16790                             month = 0;
16791                             year += 1;
16792                         } else {
16793                             month += 1;
16794                         }
16795                     }
16796                     //Roo.log([year,month,day]);
16797                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16798                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16799 //                    this.fill();
16800                     //Roo.log(this.formatDate(this.date));
16801                     this.setValue(this.formatDate(this.date));
16802                     this.hide();
16803                 }
16804                 break;
16805         }
16806     },
16807     
16808     setStartDate: function(startDate)
16809     {
16810         this.startDate = startDate || -Infinity;
16811         if (this.startDate !== -Infinity) {
16812             this.startDate = this.parseDate(this.startDate);
16813         }
16814         this.update();
16815         this.updateNavArrows();
16816     },
16817
16818     setEndDate: function(endDate)
16819     {
16820         this.endDate = endDate || Infinity;
16821         if (this.endDate !== Infinity) {
16822             this.endDate = this.parseDate(this.endDate);
16823         }
16824         this.update();
16825         this.updateNavArrows();
16826     },
16827     
16828     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16829     {
16830         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16831         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16832             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16833         }
16834         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16835             return parseInt(d, 10);
16836         });
16837         this.update();
16838         this.updateNavArrows();
16839     },
16840     
16841     updateNavArrows: function() 
16842     {
16843         if(this.singleMode){
16844             return;
16845         }
16846         
16847         var d = new Date(this.viewDate),
16848         year = d.getUTCFullYear(),
16849         month = d.getUTCMonth();
16850         
16851         Roo.each(this.picker().select('.prev', true).elements, function(v){
16852             v.show();
16853             switch (this.viewMode) {
16854                 case 0:
16855
16856                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16857                         v.hide();
16858                     }
16859                     break;
16860                 case 1:
16861                 case 2:
16862                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16863                         v.hide();
16864                     }
16865                     break;
16866             }
16867         });
16868         
16869         Roo.each(this.picker().select('.next', true).elements, function(v){
16870             v.show();
16871             switch (this.viewMode) {
16872                 case 0:
16873
16874                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16875                         v.hide();
16876                     }
16877                     break;
16878                 case 1:
16879                 case 2:
16880                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16881                         v.hide();
16882                     }
16883                     break;
16884             }
16885         })
16886     },
16887     
16888     moveMonth: function(date, dir)
16889     {
16890         if (!dir) return date;
16891         var new_date = new Date(date.valueOf()),
16892         day = new_date.getUTCDate(),
16893         month = new_date.getUTCMonth(),
16894         mag = Math.abs(dir),
16895         new_month, test;
16896         dir = dir > 0 ? 1 : -1;
16897         if (mag == 1){
16898             test = dir == -1
16899             // If going back one month, make sure month is not current month
16900             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16901             ? function(){
16902                 return new_date.getUTCMonth() == month;
16903             }
16904             // If going forward one month, make sure month is as expected
16905             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16906             : function(){
16907                 return new_date.getUTCMonth() != new_month;
16908             };
16909             new_month = month + dir;
16910             new_date.setUTCMonth(new_month);
16911             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16912             if (new_month < 0 || new_month > 11)
16913                 new_month = (new_month + 12) % 12;
16914         } else {
16915             // For magnitudes >1, move one month at a time...
16916             for (var i=0; i<mag; i++)
16917                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16918                 new_date = this.moveMonth(new_date, dir);
16919             // ...then reset the day, keeping it in the new month
16920             new_month = new_date.getUTCMonth();
16921             new_date.setUTCDate(day);
16922             test = function(){
16923                 return new_month != new_date.getUTCMonth();
16924             };
16925         }
16926         // Common date-resetting loop -- if date is beyond end of month, make it
16927         // end of month
16928         while (test()){
16929             new_date.setUTCDate(--day);
16930             new_date.setUTCMonth(new_month);
16931         }
16932         return new_date;
16933     },
16934
16935     moveYear: function(date, dir)
16936     {
16937         return this.moveMonth(date, dir*12);
16938     },
16939
16940     dateWithinRange: function(date)
16941     {
16942         return date >= this.startDate && date <= this.endDate;
16943     },
16944
16945     
16946     remove: function() 
16947     {
16948         this.picker().remove();
16949     }
16950    
16951 });
16952
16953 Roo.apply(Roo.bootstrap.DateField,  {
16954     
16955     head : {
16956         tag: 'thead',
16957         cn: [
16958         {
16959             tag: 'tr',
16960             cn: [
16961             {
16962                 tag: 'th',
16963                 cls: 'prev',
16964                 html: '<i class="fa fa-arrow-left"/>'
16965             },
16966             {
16967                 tag: 'th',
16968                 cls: 'switch',
16969                 colspan: '5'
16970             },
16971             {
16972                 tag: 'th',
16973                 cls: 'next',
16974                 html: '<i class="fa fa-arrow-right"/>'
16975             }
16976
16977             ]
16978         }
16979         ]
16980     },
16981     
16982     content : {
16983         tag: 'tbody',
16984         cn: [
16985         {
16986             tag: 'tr',
16987             cn: [
16988             {
16989                 tag: 'td',
16990                 colspan: '7'
16991             }
16992             ]
16993         }
16994         ]
16995     },
16996     
16997     footer : {
16998         tag: 'tfoot',
16999         cn: [
17000         {
17001             tag: 'tr',
17002             cn: [
17003             {
17004                 tag: 'th',
17005                 colspan: '7',
17006                 cls: 'today'
17007             }
17008                     
17009             ]
17010         }
17011         ]
17012     },
17013     
17014     dates:{
17015         en: {
17016             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17017             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17018             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17019             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17020             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17021             today: "Today"
17022         }
17023     },
17024     
17025     modes: [
17026     {
17027         clsName: 'days',
17028         navFnc: 'Month',
17029         navStep: 1
17030     },
17031     {
17032         clsName: 'months',
17033         navFnc: 'FullYear',
17034         navStep: 1
17035     },
17036     {
17037         clsName: 'years',
17038         navFnc: 'FullYear',
17039         navStep: 10
17040     }]
17041 });
17042
17043 Roo.apply(Roo.bootstrap.DateField,  {
17044   
17045     template : {
17046         tag: 'div',
17047         cls: 'datepicker dropdown-menu roo-dynamic',
17048         cn: [
17049         {
17050             tag: 'div',
17051             cls: 'datepicker-days',
17052             cn: [
17053             {
17054                 tag: 'table',
17055                 cls: 'table-condensed',
17056                 cn:[
17057                 Roo.bootstrap.DateField.head,
17058                 {
17059                     tag: 'tbody'
17060                 },
17061                 Roo.bootstrap.DateField.footer
17062                 ]
17063             }
17064             ]
17065         },
17066         {
17067             tag: 'div',
17068             cls: 'datepicker-months',
17069             cn: [
17070             {
17071                 tag: 'table',
17072                 cls: 'table-condensed',
17073                 cn:[
17074                 Roo.bootstrap.DateField.head,
17075                 Roo.bootstrap.DateField.content,
17076                 Roo.bootstrap.DateField.footer
17077                 ]
17078             }
17079             ]
17080         },
17081         {
17082             tag: 'div',
17083             cls: 'datepicker-years',
17084             cn: [
17085             {
17086                 tag: 'table',
17087                 cls: 'table-condensed',
17088                 cn:[
17089                 Roo.bootstrap.DateField.head,
17090                 Roo.bootstrap.DateField.content,
17091                 Roo.bootstrap.DateField.footer
17092                 ]
17093             }
17094             ]
17095         }
17096         ]
17097     }
17098 });
17099
17100  
17101
17102  /*
17103  * - LGPL
17104  *
17105  * TimeField
17106  * 
17107  */
17108
17109 /**
17110  * @class Roo.bootstrap.TimeField
17111  * @extends Roo.bootstrap.Input
17112  * Bootstrap DateField class
17113  * 
17114  * 
17115  * @constructor
17116  * Create a new TimeField
17117  * @param {Object} config The config object
17118  */
17119
17120 Roo.bootstrap.TimeField = function(config){
17121     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17122     this.addEvents({
17123             /**
17124              * @event show
17125              * Fires when this field show.
17126              * @param {Roo.bootstrap.DateField} thisthis
17127              * @param {Mixed} date The date value
17128              */
17129             show : true,
17130             /**
17131              * @event show
17132              * Fires when this field hide.
17133              * @param {Roo.bootstrap.DateField} this
17134              * @param {Mixed} date The date value
17135              */
17136             hide : true,
17137             /**
17138              * @event select
17139              * Fires when select a date.
17140              * @param {Roo.bootstrap.DateField} this
17141              * @param {Mixed} date The date value
17142              */
17143             select : true
17144         });
17145 };
17146
17147 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17148     
17149     /**
17150      * @cfg {String} format
17151      * The default time format string which can be overriden for localization support.  The format must be
17152      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17153      */
17154     format : "H:i",
17155        
17156     onRender: function(ct, position)
17157     {
17158         
17159         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17160                 
17161         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17162         
17163         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17164         
17165         this.pop = this.picker().select('>.datepicker-time',true).first();
17166         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17167         
17168         this.picker().on('mousedown', this.onMousedown, this);
17169         this.picker().on('click', this.onClick, this);
17170         
17171         this.picker().addClass('datepicker-dropdown');
17172     
17173         this.fillTime();
17174         this.update();
17175             
17176         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17177         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17178         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17179         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17180         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17181         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17182
17183     },
17184     
17185     fireKey: function(e){
17186         if (!this.picker().isVisible()){
17187             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17188                 this.show();
17189             }
17190             return;
17191         }
17192
17193         e.preventDefault();
17194         
17195         switch(e.keyCode){
17196             case 27: // escape
17197                 this.hide();
17198                 break;
17199             case 37: // left
17200             case 39: // right
17201                 this.onTogglePeriod();
17202                 break;
17203             case 38: // up
17204                 this.onIncrementMinutes();
17205                 break;
17206             case 40: // down
17207                 this.onDecrementMinutes();
17208                 break;
17209             case 13: // enter
17210             case 9: // tab
17211                 this.setTime();
17212                 break;
17213         }
17214     },
17215     
17216     onClick: function(e) {
17217         e.stopPropagation();
17218         e.preventDefault();
17219     },
17220     
17221     picker : function()
17222     {
17223         return this.el.select('.datepicker', true).first();
17224     },
17225     
17226     fillTime: function()
17227     {    
17228         var time = this.pop.select('tbody', true).first();
17229         
17230         time.dom.innerHTML = '';
17231         
17232         time.createChild({
17233             tag: 'tr',
17234             cn: [
17235                 {
17236                     tag: 'td',
17237                     cn: [
17238                         {
17239                             tag: 'a',
17240                             href: '#',
17241                             cls: 'btn',
17242                             cn: [
17243                                 {
17244                                     tag: 'span',
17245                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17246                                 }
17247                             ]
17248                         } 
17249                     ]
17250                 },
17251                 {
17252                     tag: 'td',
17253                     cls: 'separator'
17254                 },
17255                 {
17256                     tag: 'td',
17257                     cn: [
17258                         {
17259                             tag: 'a',
17260                             href: '#',
17261                             cls: 'btn',
17262                             cn: [
17263                                 {
17264                                     tag: 'span',
17265                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17266                                 }
17267                             ]
17268                         }
17269                     ]
17270                 },
17271                 {
17272                     tag: 'td',
17273                     cls: 'separator'
17274                 }
17275             ]
17276         });
17277         
17278         time.createChild({
17279             tag: 'tr',
17280             cn: [
17281                 {
17282                     tag: 'td',
17283                     cn: [
17284                         {
17285                             tag: 'span',
17286                             cls: 'timepicker-hour',
17287                             html: '00'
17288                         }  
17289                     ]
17290                 },
17291                 {
17292                     tag: 'td',
17293                     cls: 'separator',
17294                     html: ':'
17295                 },
17296                 {
17297                     tag: 'td',
17298                     cn: [
17299                         {
17300                             tag: 'span',
17301                             cls: 'timepicker-minute',
17302                             html: '00'
17303                         }  
17304                     ]
17305                 },
17306                 {
17307                     tag: 'td',
17308                     cls: 'separator'
17309                 },
17310                 {
17311                     tag: 'td',
17312                     cn: [
17313                         {
17314                             tag: 'button',
17315                             type: 'button',
17316                             cls: 'btn btn-primary period',
17317                             html: 'AM'
17318                             
17319                         }
17320                     ]
17321                 }
17322             ]
17323         });
17324         
17325         time.createChild({
17326             tag: 'tr',
17327             cn: [
17328                 {
17329                     tag: 'td',
17330                     cn: [
17331                         {
17332                             tag: 'a',
17333                             href: '#',
17334                             cls: 'btn',
17335                             cn: [
17336                                 {
17337                                     tag: 'span',
17338                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17339                                 }
17340                             ]
17341                         }
17342                     ]
17343                 },
17344                 {
17345                     tag: 'td',
17346                     cls: 'separator'
17347                 },
17348                 {
17349                     tag: 'td',
17350                     cn: [
17351                         {
17352                             tag: 'a',
17353                             href: '#',
17354                             cls: 'btn',
17355                             cn: [
17356                                 {
17357                                     tag: 'span',
17358                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17359                                 }
17360                             ]
17361                         }
17362                     ]
17363                 },
17364                 {
17365                     tag: 'td',
17366                     cls: 'separator'
17367                 }
17368             ]
17369         });
17370         
17371     },
17372     
17373     update: function()
17374     {
17375         
17376         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17377         
17378         this.fill();
17379     },
17380     
17381     fill: function() 
17382     {
17383         var hours = this.time.getHours();
17384         var minutes = this.time.getMinutes();
17385         var period = 'AM';
17386         
17387         if(hours > 11){
17388             period = 'PM';
17389         }
17390         
17391         if(hours == 0){
17392             hours = 12;
17393         }
17394         
17395         
17396         if(hours > 12){
17397             hours = hours - 12;
17398         }
17399         
17400         if(hours < 10){
17401             hours = '0' + hours;
17402         }
17403         
17404         if(minutes < 10){
17405             minutes = '0' + minutes;
17406         }
17407         
17408         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17409         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17410         this.pop.select('button', true).first().dom.innerHTML = period;
17411         
17412     },
17413     
17414     place: function()
17415     {   
17416         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17417         
17418         var cls = ['bottom'];
17419         
17420         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17421             cls.pop();
17422             cls.push('top');
17423         }
17424         
17425         cls.push('right');
17426         
17427         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17428             cls.pop();
17429             cls.push('left');
17430         }
17431         
17432         this.picker().addClass(cls.join('-'));
17433         
17434         var _this = this;
17435         
17436         Roo.each(cls, function(c){
17437             if(c == 'bottom'){
17438                 _this.picker().setTop(_this.inputEl().getHeight());
17439                 return;
17440             }
17441             if(c == 'top'){
17442                 _this.picker().setTop(0 - _this.picker().getHeight());
17443                 return;
17444             }
17445             
17446             if(c == 'left'){
17447                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17448                 return;
17449             }
17450             if(c == 'right'){
17451                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17452                 return;
17453             }
17454         });
17455         
17456     },
17457   
17458     onFocus : function()
17459     {
17460         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17461         this.show();
17462     },
17463     
17464     onBlur : function()
17465     {
17466         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17467         this.hide();
17468     },
17469     
17470     show : function()
17471     {
17472         this.picker().show();
17473         this.pop.show();
17474         this.update();
17475         this.place();
17476         
17477         this.fireEvent('show', this, this.date);
17478     },
17479     
17480     hide : function()
17481     {
17482         this.picker().hide();
17483         this.pop.hide();
17484         
17485         this.fireEvent('hide', this, this.date);
17486     },
17487     
17488     setTime : function()
17489     {
17490         this.hide();
17491         this.setValue(this.time.format(this.format));
17492         
17493         this.fireEvent('select', this, this.date);
17494         
17495         
17496     },
17497     
17498     onMousedown: function(e){
17499         e.stopPropagation();
17500         e.preventDefault();
17501     },
17502     
17503     onIncrementHours: function()
17504     {
17505         Roo.log('onIncrementHours');
17506         this.time = this.time.add(Date.HOUR, 1);
17507         this.update();
17508         
17509     },
17510     
17511     onDecrementHours: function()
17512     {
17513         Roo.log('onDecrementHours');
17514         this.time = this.time.add(Date.HOUR, -1);
17515         this.update();
17516     },
17517     
17518     onIncrementMinutes: function()
17519     {
17520         Roo.log('onIncrementMinutes');
17521         this.time = this.time.add(Date.MINUTE, 1);
17522         this.update();
17523     },
17524     
17525     onDecrementMinutes: function()
17526     {
17527         Roo.log('onDecrementMinutes');
17528         this.time = this.time.add(Date.MINUTE, -1);
17529         this.update();
17530     },
17531     
17532     onTogglePeriod: function()
17533     {
17534         Roo.log('onTogglePeriod');
17535         this.time = this.time.add(Date.HOUR, 12);
17536         this.update();
17537     }
17538     
17539    
17540 });
17541
17542 Roo.apply(Roo.bootstrap.TimeField,  {
17543     
17544     content : {
17545         tag: 'tbody',
17546         cn: [
17547             {
17548                 tag: 'tr',
17549                 cn: [
17550                 {
17551                     tag: 'td',
17552                     colspan: '7'
17553                 }
17554                 ]
17555             }
17556         ]
17557     },
17558     
17559     footer : {
17560         tag: 'tfoot',
17561         cn: [
17562             {
17563                 tag: 'tr',
17564                 cn: [
17565                 {
17566                     tag: 'th',
17567                     colspan: '7',
17568                     cls: '',
17569                     cn: [
17570                         {
17571                             tag: 'button',
17572                             cls: 'btn btn-info ok',
17573                             html: 'OK'
17574                         }
17575                     ]
17576                 }
17577
17578                 ]
17579             }
17580         ]
17581     }
17582 });
17583
17584 Roo.apply(Roo.bootstrap.TimeField,  {
17585   
17586     template : {
17587         tag: 'div',
17588         cls: 'datepicker dropdown-menu',
17589         cn: [
17590             {
17591                 tag: 'div',
17592                 cls: 'datepicker-time',
17593                 cn: [
17594                 {
17595                     tag: 'table',
17596                     cls: 'table-condensed',
17597                     cn:[
17598                     Roo.bootstrap.TimeField.content,
17599                     Roo.bootstrap.TimeField.footer
17600                     ]
17601                 }
17602                 ]
17603             }
17604         ]
17605     }
17606 });
17607
17608  
17609
17610  /*
17611  * - LGPL
17612  *
17613  * MonthField
17614  * 
17615  */
17616
17617 /**
17618  * @class Roo.bootstrap.MonthField
17619  * @extends Roo.bootstrap.Input
17620  * Bootstrap MonthField class
17621  * 
17622  * @cfg {String} language default en
17623  * 
17624  * @constructor
17625  * Create a new MonthField
17626  * @param {Object} config The config object
17627  */
17628
17629 Roo.bootstrap.MonthField = function(config){
17630     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17631     
17632     this.addEvents({
17633         /**
17634          * @event show
17635          * Fires when this field show.
17636          * @param {Roo.bootstrap.MonthField} this
17637          * @param {Mixed} date The date value
17638          */
17639         show : true,
17640         /**
17641          * @event show
17642          * Fires when this field hide.
17643          * @param {Roo.bootstrap.MonthField} this
17644          * @param {Mixed} date The date value
17645          */
17646         hide : true,
17647         /**
17648          * @event select
17649          * Fires when select a date.
17650          * @param {Roo.bootstrap.MonthField} this
17651          * @param {String} oldvalue The old value
17652          * @param {String} newvalue The new value
17653          */
17654         select : true
17655     });
17656 };
17657
17658 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17659     
17660     onRender: function(ct, position)
17661     {
17662         
17663         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17664         
17665         this.language = this.language || 'en';
17666         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17667         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17668         
17669         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17670         this.isInline = false;
17671         this.isInput = true;
17672         this.component = this.el.select('.add-on', true).first() || false;
17673         this.component = (this.component && this.component.length === 0) ? false : this.component;
17674         this.hasInput = this.component && this.inputEL().length;
17675         
17676         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17677         
17678         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17679         
17680         this.picker().on('mousedown', this.onMousedown, this);
17681         this.picker().on('click', this.onClick, this);
17682         
17683         this.picker().addClass('datepicker-dropdown');
17684         
17685         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17686             v.setStyle('width', '189px');
17687         });
17688         
17689         this.fillMonths();
17690         
17691         this.update();
17692         
17693         if(this.isInline) {
17694             this.show();
17695         }
17696         
17697     },
17698     
17699     setValue: function(v, suppressEvent)
17700     {   
17701         var o = this.getValue();
17702         
17703         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17704         
17705         this.update();
17706
17707         if(suppressEvent !== true){
17708             this.fireEvent('select', this, o, v);
17709         }
17710         
17711     },
17712     
17713     getValue: function()
17714     {
17715         return this.value;
17716     },
17717     
17718     onClick: function(e) 
17719     {
17720         e.stopPropagation();
17721         e.preventDefault();
17722         
17723         var target = e.getTarget();
17724         
17725         if(target.nodeName.toLowerCase() === 'i'){
17726             target = Roo.get(target).dom.parentNode;
17727         }
17728         
17729         var nodeName = target.nodeName;
17730         var className = target.className;
17731         var html = target.innerHTML;
17732         
17733         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17734             return;
17735         }
17736         
17737         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17738         
17739         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17740         
17741         this.hide();
17742                         
17743     },
17744     
17745     picker : function()
17746     {
17747         return this.pickerEl;
17748     },
17749     
17750     fillMonths: function()
17751     {    
17752         var i = 0;
17753         var months = this.picker().select('>.datepicker-months td', true).first();
17754         
17755         months.dom.innerHTML = '';
17756         
17757         while (i < 12) {
17758             var month = {
17759                 tag: 'span',
17760                 cls: 'month',
17761                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17762             }
17763             
17764             months.createChild(month);
17765         }
17766         
17767     },
17768     
17769     update: function()
17770     {
17771         var _this = this;
17772         
17773         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17774             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17775         }
17776         
17777         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17778             e.removeClass('active');
17779             
17780             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17781                 e.addClass('active');
17782             }
17783         })
17784     },
17785     
17786     place: function()
17787     {
17788         if(this.isInline) return;
17789         
17790         this.picker().removeClass(['bottom', 'top']);
17791         
17792         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17793             /*
17794              * place to the top of element!
17795              *
17796              */
17797             
17798             this.picker().addClass('top');
17799             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17800             
17801             return;
17802         }
17803         
17804         this.picker().addClass('bottom');
17805         
17806         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17807     },
17808     
17809     onFocus : function()
17810     {
17811         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17812         this.show();
17813     },
17814     
17815     onBlur : function()
17816     {
17817         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17818         
17819         var d = this.inputEl().getValue();
17820         
17821         this.setValue(d);
17822                 
17823         this.hide();
17824     },
17825     
17826     show : function()
17827     {
17828         this.picker().show();
17829         this.picker().select('>.datepicker-months', true).first().show();
17830         this.update();
17831         this.place();
17832         
17833         this.fireEvent('show', this, this.date);
17834     },
17835     
17836     hide : function()
17837     {
17838         if(this.isInline) return;
17839         this.picker().hide();
17840         this.fireEvent('hide', this, this.date);
17841         
17842     },
17843     
17844     onMousedown: function(e)
17845     {
17846         e.stopPropagation();
17847         e.preventDefault();
17848     },
17849     
17850     keyup: function(e)
17851     {
17852         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17853         this.update();
17854     },
17855
17856     fireKey: function(e)
17857     {
17858         if (!this.picker().isVisible()){
17859             if (e.keyCode == 27) // allow escape to hide and re-show picker
17860                 this.show();
17861             return;
17862         }
17863         
17864         var dir;
17865         
17866         switch(e.keyCode){
17867             case 27: // escape
17868                 this.hide();
17869                 e.preventDefault();
17870                 break;
17871             case 37: // left
17872             case 39: // right
17873                 dir = e.keyCode == 37 ? -1 : 1;
17874                 
17875                 this.vIndex = this.vIndex + dir;
17876                 
17877                 if(this.vIndex < 0){
17878                     this.vIndex = 0;
17879                 }
17880                 
17881                 if(this.vIndex > 11){
17882                     this.vIndex = 11;
17883                 }
17884                 
17885                 if(isNaN(this.vIndex)){
17886                     this.vIndex = 0;
17887                 }
17888                 
17889                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17890                 
17891                 break;
17892             case 38: // up
17893             case 40: // down
17894                 
17895                 dir = e.keyCode == 38 ? -1 : 1;
17896                 
17897                 this.vIndex = this.vIndex + dir * 4;
17898                 
17899                 if(this.vIndex < 0){
17900                     this.vIndex = 0;
17901                 }
17902                 
17903                 if(this.vIndex > 11){
17904                     this.vIndex = 11;
17905                 }
17906                 
17907                 if(isNaN(this.vIndex)){
17908                     this.vIndex = 0;
17909                 }
17910                 
17911                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17912                 break;
17913                 
17914             case 13: // enter
17915                 
17916                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17917                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17918                 }
17919                 
17920                 this.hide();
17921                 e.preventDefault();
17922                 break;
17923             case 9: // tab
17924                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17925                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17926                 }
17927                 this.hide();
17928                 break;
17929             case 16: // shift
17930             case 17: // ctrl
17931             case 18: // alt
17932                 break;
17933             default :
17934                 this.hide();
17935                 
17936         }
17937     },
17938     
17939     remove: function() 
17940     {
17941         this.picker().remove();
17942     }
17943    
17944 });
17945
17946 Roo.apply(Roo.bootstrap.MonthField,  {
17947     
17948     content : {
17949         tag: 'tbody',
17950         cn: [
17951         {
17952             tag: 'tr',
17953             cn: [
17954             {
17955                 tag: 'td',
17956                 colspan: '7'
17957             }
17958             ]
17959         }
17960         ]
17961     },
17962     
17963     dates:{
17964         en: {
17965             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17966             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17967         }
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.MonthField,  {
17972   
17973     template : {
17974         tag: 'div',
17975         cls: 'datepicker dropdown-menu roo-dynamic',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'datepicker-months',
17980                 cn: [
17981                 {
17982                     tag: 'table',
17983                     cls: 'table-condensed',
17984                     cn:[
17985                         Roo.bootstrap.DateField.content
17986                     ]
17987                 }
17988                 ]
17989             }
17990         ]
17991     }
17992 });
17993
17994  
17995
17996  
17997  /*
17998  * - LGPL
17999  *
18000  * CheckBox
18001  * 
18002  */
18003
18004 /**
18005  * @class Roo.bootstrap.CheckBox
18006  * @extends Roo.bootstrap.Input
18007  * Bootstrap CheckBox class
18008  * 
18009  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18010  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18011  * @cfg {String} boxLabel The text that appears beside the checkbox
18012  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18013  * @cfg {Boolean} checked initnal the element
18014  * @cfg {Boolean} inline inline the element (default false)
18015  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18016  * 
18017  * @constructor
18018  * Create a new CheckBox
18019  * @param {Object} config The config object
18020  */
18021
18022 Roo.bootstrap.CheckBox = function(config){
18023     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18024    
18025     this.addEvents({
18026         /**
18027         * @event check
18028         * Fires when the element is checked or unchecked.
18029         * @param {Roo.bootstrap.CheckBox} this This input
18030         * @param {Boolean} checked The new checked value
18031         */
18032        check : true
18033     });
18034     
18035 };
18036
18037 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18038   
18039     inputType: 'checkbox',
18040     inputValue: 1,
18041     valueOff: 0,
18042     boxLabel: false,
18043     checked: false,
18044     weight : false,
18045     inline: false,
18046     
18047     getAutoCreate : function()
18048     {
18049         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18050         
18051         var id = Roo.id();
18052         
18053         var cfg = {};
18054         
18055         cfg.cls = 'form-group ' + this.inputType; //input-group
18056         
18057         if(this.inline){
18058             cfg.cls += ' ' + this.inputType + '-inline';
18059         }
18060         
18061         var input =  {
18062             tag: 'input',
18063             id : id,
18064             type : this.inputType,
18065             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18066             cls : 'roo-' + this.inputType, //'form-box',
18067             placeholder : this.placeholder || ''
18068             
18069         };
18070         
18071         if (this.weight) { // Validity check?
18072             cfg.cls += " " + this.inputType + "-" + this.weight;
18073         }
18074         
18075         if (this.disabled) {
18076             input.disabled=true;
18077         }
18078         
18079         if(this.checked){
18080             input.checked = this.checked;
18081         }
18082         
18083         if (this.name) {
18084             input.name = this.name;
18085         }
18086         
18087         if (this.size) {
18088             input.cls += ' input-' + this.size;
18089         }
18090         
18091         var settings=this;
18092         
18093         ['xs','sm','md','lg'].map(function(size){
18094             if (settings[size]) {
18095                 cfg.cls += ' col-' + size + '-' + settings[size];
18096             }
18097         });
18098         
18099         var inputblock = input;
18100          
18101         if (this.before || this.after) {
18102             
18103             inputblock = {
18104                 cls : 'input-group',
18105                 cn :  [] 
18106             };
18107             
18108             if (this.before) {
18109                 inputblock.cn.push({
18110                     tag :'span',
18111                     cls : 'input-group-addon',
18112                     html : this.before
18113                 });
18114             }
18115             
18116             inputblock.cn.push(input);
18117             
18118             if (this.after) {
18119                 inputblock.cn.push({
18120                     tag :'span',
18121                     cls : 'input-group-addon',
18122                     html : this.after
18123                 });
18124             }
18125             
18126         }
18127         
18128         if (align ==='left' && this.fieldLabel.length) {
18129                 Roo.log("left and has label");
18130                 cfg.cn = [
18131                     
18132                     {
18133                         tag: 'label',
18134                         'for' :  id,
18135                         cls : 'control-label col-md-' + this.labelWidth,
18136                         html : this.fieldLabel
18137                         
18138                     },
18139                     {
18140                         cls : "col-md-" + (12 - this.labelWidth), 
18141                         cn: [
18142                             inputblock
18143                         ]
18144                     }
18145                     
18146                 ];
18147         } else if ( this.fieldLabel.length) {
18148                 Roo.log(" label");
18149                 cfg.cn = [
18150                    
18151                     {
18152                         tag: this.boxLabel ? 'span' : 'label',
18153                         'for': id,
18154                         cls: 'control-label box-input-label',
18155                         //cls : 'input-group-addon',
18156                         html : this.fieldLabel
18157                         
18158                     },
18159                     
18160                     inputblock
18161                     
18162                 ];
18163
18164         } else {
18165             
18166                 Roo.log(" no label && no align");
18167                 cfg.cn = [  inputblock ] ;
18168                 
18169                 
18170         }
18171         if(this.boxLabel){
18172              var boxLabelCfg = {
18173                 tag: 'label',
18174                 //'for': id, // box label is handled by onclick - so no for...
18175                 cls: 'box-label',
18176                 html: this.boxLabel
18177             }
18178             
18179             if(this.tooltip){
18180                 boxLabelCfg.tooltip = this.tooltip;
18181             }
18182              
18183             cfg.cn.push(boxLabelCfg);
18184         }
18185         
18186         
18187        
18188         return cfg;
18189         
18190     },
18191     
18192     /**
18193      * return the real input element.
18194      */
18195     inputEl: function ()
18196     {
18197         return this.el.select('input.roo-' + this.inputType,true).first();
18198     },
18199     
18200     labelEl: function()
18201     {
18202         return this.el.select('label.control-label',true).first();
18203     },
18204     /* depricated... */
18205     
18206     label: function()
18207     {
18208         return this.labelEl();
18209     },
18210     
18211     initEvents : function()
18212     {
18213 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18214         
18215         this.inputEl().on('click', this.onClick,  this);
18216         
18217         if (this.boxLabel) { 
18218             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18219         }
18220         
18221         this.startValue = this.getValue();
18222         
18223         if(this.groupId){
18224             Roo.bootstrap.CheckBox.register(this);
18225         }
18226     },
18227     
18228     onClick : function()
18229     {   
18230         this.setChecked(!this.checked);
18231     },
18232     
18233     setChecked : function(state,suppressEvent)
18234     {
18235         this.startValue = this.getValue();
18236         
18237         if(this.inputType == 'radio'){
18238             
18239             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18240                 e.dom.checked = false;
18241             });
18242             
18243             this.inputEl().dom.checked = true;
18244             
18245             this.inputEl().dom.value = this.inputValue;
18246             
18247             if(suppressEvent !== true){
18248                 this.fireEvent('check', this, true);
18249             }
18250             
18251             this.validate();
18252             
18253             return;
18254         }
18255         
18256         this.checked = state;
18257         
18258         this.inputEl().dom.checked = state;
18259         
18260         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18261         
18262         if(suppressEvent !== true){
18263             this.fireEvent('check', this, state);
18264         }
18265         
18266         this.validate();
18267     },
18268     
18269     getValue : function()
18270     {
18271         if(this.inputType == 'radio'){
18272             return this.getGroupValue();
18273         }
18274         
18275         return this.inputEl().getValue();
18276         
18277     },
18278     
18279     getGroupValue : function()
18280     {
18281         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18282             return '';
18283         }
18284         
18285         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18286     },
18287     
18288     setValue : function(v,suppressEvent)
18289     {
18290         if(this.inputType == 'radio'){
18291             this.setGroupValue(v, suppressEvent);
18292             return;
18293         }
18294         
18295         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18296         
18297         this.validate();
18298     },
18299     
18300     setGroupValue : function(v, suppressEvent)
18301     {
18302         this.startValue = this.getValue();
18303         
18304         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18305             e.dom.checked = false;
18306             
18307             if(e.dom.value == v){
18308                 e.dom.checked = true;
18309             }
18310         });
18311         
18312         if(suppressEvent !== true){
18313             this.fireEvent('check', this, true);
18314         }
18315
18316         this.validate();
18317         
18318         return;
18319     },
18320     
18321     validate : function()
18322     {
18323         if(
18324                 this.disabled || 
18325                 (this.inputType == 'radio' && this.validateRadio()) ||
18326                 (this.inputType == 'checkbox' && this.validateCheckbox())
18327         ){
18328             this.markValid();
18329             return true;
18330         }
18331         
18332         this.markInvalid();
18333         return false;
18334     },
18335     
18336     validateRadio : function()
18337     {
18338         var valid = false;
18339         
18340         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18341             if(!e.dom.checked){
18342                 return;
18343             }
18344             
18345             valid = true;
18346             
18347             return false;
18348         });
18349         
18350         return valid;
18351     },
18352     
18353     validateCheckbox : function()
18354     {
18355         if(!this.groupId){
18356             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18357         }
18358         
18359         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18360         
18361         if(!group){
18362             return false;
18363         }
18364         
18365         var r = false;
18366         
18367         for(var i in group){
18368             if(r){
18369                 break;
18370             }
18371             
18372             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18373         }
18374         
18375         return r;
18376     },
18377     
18378     /**
18379      * Mark this field as valid
18380      */
18381     markValid : function()
18382     {
18383         if(this.allowBlank){
18384             return;
18385         }
18386         
18387         var _this = this;
18388         
18389         this.fireEvent('valid', this);
18390         
18391         if(this.inputType == 'radio'){
18392             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18393                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18394                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18395             });
18396             
18397             return;
18398         }
18399         
18400         if(!this.groupId){
18401             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18402             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18403             return;
18404         }
18405         
18406         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18407             
18408         if(!group){
18409             return;
18410         }
18411         
18412         for(var i in group){
18413             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18414             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18415         }
18416     },
18417     
18418      /**
18419      * Mark this field as invalid
18420      * @param {String} msg The validation message
18421      */
18422     markInvalid : function(msg)
18423     {
18424         if(this.allowBlank){
18425             return;
18426         }
18427         
18428         var _this = this;
18429         
18430         this.fireEvent('invalid', this, msg);
18431         
18432         if(this.inputType == 'radio'){
18433             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18434                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18435                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18436             });
18437             
18438             return;
18439         }
18440         
18441         if(!this.groupId){
18442             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18443             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18444             return;
18445         }
18446         
18447         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18448             
18449         if(!group){
18450             return;
18451         }
18452         
18453         for(var i in group){
18454             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18455             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18456         }
18457         
18458     }
18459     
18460 });
18461
18462 Roo.apply(Roo.bootstrap.CheckBox, {
18463     
18464     groups: {},
18465     
18466      /**
18467     * register a CheckBox Group
18468     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18469     */
18470     register : function(checkbox)
18471     {
18472         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18473             this.groups[checkbox.groupId] = {};
18474         }
18475         
18476         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18477             return;
18478         }
18479         
18480         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18481         
18482     },
18483     /**
18484     * fetch a CheckBox Group based on the group ID
18485     * @param {string} the group ID
18486     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18487     */
18488     get: function(groupId) {
18489         if (typeof(this.groups[groupId]) == 'undefined') {
18490             return false;
18491         }
18492         
18493         return this.groups[groupId] ;
18494     }
18495     
18496     
18497 });
18498 /*
18499  * - LGPL
18500  *
18501  * Radio
18502  *
18503  *
18504  * not inline
18505  *<div class="radio">
18506   <label>
18507     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18508     Option one is this and that&mdash;be sure to include why it's great
18509   </label>
18510 </div>
18511  *
18512  *
18513  *inline
18514  *<span>
18515  *<label class="radio-inline">fieldLabel</label>
18516  *<label class="radio-inline">
18517   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18518 </label>
18519 <span>
18520  * 
18521  * 
18522  */
18523
18524 /**
18525  * @class Roo.bootstrap.Radio
18526  * @extends Roo.bootstrap.CheckBox
18527  * Bootstrap Radio class
18528
18529  * @constructor
18530  * Create a new Radio
18531  * @param {Object} config The config object
18532  */
18533
18534 Roo.bootstrap.Radio = function(config){
18535     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18536    
18537 };
18538
18539 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18540     
18541     inputType: 'radio',
18542     inputValue: '',
18543     valueOff: '',
18544     
18545     getAutoCreate : function()
18546     {
18547         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18548         align = align || 'left'; // default...
18549         
18550         
18551         
18552         var id = Roo.id();
18553         
18554         var cfg = {
18555                 tag : this.inline ? 'span' : 'div',
18556                 cls : '',
18557                 cn : []
18558         };
18559         
18560         var inline = this.inline ? ' radio-inline' : '';
18561         
18562         var lbl = {
18563                 tag: 'label' ,
18564                 // does not need for, as we wrap the input with it..
18565                 'for' : id,
18566                 cls : 'control-label box-label' + inline,
18567                 cn : []
18568         };
18569         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18570         
18571         var fieldLabel = {
18572             tag: 'label' ,
18573             //cls : 'control-label' + inline,
18574             html : this.fieldLabel,
18575             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18576         };
18577         
18578  
18579         
18580         
18581         var input =  {
18582             tag: 'input',
18583             id : id,
18584             type : this.inputType,
18585             //value : (!this.checked) ? this.valueOff : this.inputValue,
18586             value : this.inputValue,
18587             cls : 'roo-radio',
18588             placeholder : this.placeholder || '' // ?? needed????
18589             
18590         };
18591         if (this.weight) { // Validity check?
18592             input.cls += " radio-" + this.weight;
18593         }
18594         if (this.disabled) {
18595             input.disabled=true;
18596         }
18597         
18598         if(this.checked){
18599             input.checked = this.checked;
18600         }
18601         
18602         if (this.name) {
18603             input.name = this.name;
18604         }
18605         
18606         if (this.size) {
18607             input.cls += ' input-' + this.size;
18608         }
18609         
18610         //?? can span's inline have a width??
18611         
18612         var settings=this;
18613         ['xs','sm','md','lg'].map(function(size){
18614             if (settings[size]) {
18615                 cfg.cls += ' col-' + size + '-' + settings[size];
18616             }
18617         });
18618         
18619         var inputblock = input;
18620         
18621         if (this.before || this.after) {
18622             
18623             inputblock = {
18624                 cls : 'input-group',
18625                 tag : 'span',
18626                 cn :  [] 
18627             };
18628             if (this.before) {
18629                 inputblock.cn.push({
18630                     tag :'span',
18631                     cls : 'input-group-addon',
18632                     html : this.before
18633                 });
18634             }
18635             inputblock.cn.push(input);
18636             if (this.after) {
18637                 inputblock.cn.push({
18638                     tag :'span',
18639                     cls : 'input-group-addon',
18640                     html : this.after
18641                 });
18642             }
18643             
18644         };
18645         
18646         
18647         if (this.fieldLabel && this.fieldLabel.length) {
18648             cfg.cn.push(fieldLabel);
18649         }
18650        
18651         // normal bootstrap puts the input inside the label.
18652         // however with our styled version - it has to go after the input.
18653        
18654         //lbl.cn.push(inputblock);
18655         
18656         var lblwrap =  {
18657             tag: 'span',
18658             cls: 'radio' + inline,
18659             cn: [
18660                 inputblock,
18661                 lbl
18662             ]
18663         };
18664         
18665         cfg.cn.push( lblwrap);
18666         
18667         if(this.boxLabel){
18668             lbl.cn.push({
18669                 tag: 'span',
18670                 html: this.boxLabel
18671             })
18672         }
18673          
18674         
18675         return cfg;
18676         
18677     },
18678     
18679     initEvents : function()
18680     {
18681 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18682         
18683         this.inputEl().on('click', this.onClick,  this);
18684         if (this.boxLabel) {
18685             Roo.log('find label')
18686             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18687         }
18688         
18689     },
18690     
18691     inputEl: function ()
18692     {
18693         return this.el.select('input.roo-radio',true).first();
18694     },
18695     onClick : function()
18696     {   
18697         Roo.log("click");
18698         this.setChecked(true);
18699     },
18700     
18701     setChecked : function(state,suppressEvent)
18702     {
18703         if(state){
18704             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18705                 v.dom.checked = false;
18706             });
18707         }
18708         Roo.log(this.inputEl().dom);
18709         this.checked = state;
18710         this.inputEl().dom.checked = state;
18711         
18712         if(suppressEvent !== true){
18713             this.fireEvent('check', this, state);
18714         }
18715         
18716         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18717         
18718     },
18719     
18720     getGroupValue : function()
18721     {
18722         var value = '';
18723         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18724             if(v.dom.checked == true){
18725                 value = v.dom.value;
18726             }
18727         });
18728         
18729         return value;
18730     },
18731     
18732     /**
18733      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18734      * @return {Mixed} value The field value
18735      */
18736     getValue : function(){
18737         return this.getGroupValue();
18738     }
18739     
18740 });
18741
18742  
18743 //<script type="text/javascript">
18744
18745 /*
18746  * Based  Ext JS Library 1.1.1
18747  * Copyright(c) 2006-2007, Ext JS, LLC.
18748  * LGPL
18749  *
18750  */
18751  
18752 /**
18753  * @class Roo.HtmlEditorCore
18754  * @extends Roo.Component
18755  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18756  *
18757  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18758  */
18759
18760 Roo.HtmlEditorCore = function(config){
18761     
18762     
18763     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18764     
18765     
18766     this.addEvents({
18767         /**
18768          * @event initialize
18769          * Fires when the editor is fully initialized (including the iframe)
18770          * @param {Roo.HtmlEditorCore} this
18771          */
18772         initialize: true,
18773         /**
18774          * @event activate
18775          * Fires when the editor is first receives the focus. Any insertion must wait
18776          * until after this event.
18777          * @param {Roo.HtmlEditorCore} this
18778          */
18779         activate: true,
18780          /**
18781          * @event beforesync
18782          * Fires before the textarea is updated with content from the editor iframe. Return false
18783          * to cancel the sync.
18784          * @param {Roo.HtmlEditorCore} this
18785          * @param {String} html
18786          */
18787         beforesync: true,
18788          /**
18789          * @event beforepush
18790          * Fires before the iframe editor is updated with content from the textarea. Return false
18791          * to cancel the push.
18792          * @param {Roo.HtmlEditorCore} this
18793          * @param {String} html
18794          */
18795         beforepush: true,
18796          /**
18797          * @event sync
18798          * Fires when the textarea is updated with content from the editor iframe.
18799          * @param {Roo.HtmlEditorCore} this
18800          * @param {String} html
18801          */
18802         sync: true,
18803          /**
18804          * @event push
18805          * Fires when the iframe editor is updated with content from the textarea.
18806          * @param {Roo.HtmlEditorCore} this
18807          * @param {String} html
18808          */
18809         push: true,
18810         
18811         /**
18812          * @event editorevent
18813          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18814          * @param {Roo.HtmlEditorCore} this
18815          */
18816         editorevent: true
18817         
18818     });
18819     
18820     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18821     
18822     // defaults : white / black...
18823     this.applyBlacklists();
18824     
18825     
18826     
18827 };
18828
18829
18830 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18831
18832
18833      /**
18834      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18835      */
18836     
18837     owner : false,
18838     
18839      /**
18840      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18841      *                        Roo.resizable.
18842      */
18843     resizable : false,
18844      /**
18845      * @cfg {Number} height (in pixels)
18846      */   
18847     height: 300,
18848    /**
18849      * @cfg {Number} width (in pixels)
18850      */   
18851     width: 500,
18852     
18853     /**
18854      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18855      * 
18856      */
18857     stylesheets: false,
18858     
18859     // id of frame..
18860     frameId: false,
18861     
18862     // private properties
18863     validationEvent : false,
18864     deferHeight: true,
18865     initialized : false,
18866     activated : false,
18867     sourceEditMode : false,
18868     onFocus : Roo.emptyFn,
18869     iframePad:3,
18870     hideMode:'offsets',
18871     
18872     clearUp: true,
18873     
18874     // blacklist + whitelisted elements..
18875     black: false,
18876     white: false,
18877      
18878     
18879
18880     /**
18881      * Protected method that will not generally be called directly. It
18882      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18883      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18884      */
18885     getDocMarkup : function(){
18886         // body styles..
18887         var st = '';
18888         
18889         // inherit styels from page...?? 
18890         if (this.stylesheets === false) {
18891             
18892             Roo.get(document.head).select('style').each(function(node) {
18893                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18894             });
18895             
18896             Roo.get(document.head).select('link').each(function(node) { 
18897                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18898             });
18899             
18900         } else if (!this.stylesheets.length) {
18901                 // simple..
18902                 st = '<style type="text/css">' +
18903                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18904                    '</style>';
18905         } else { 
18906             
18907         }
18908         
18909         st +=  '<style type="text/css">' +
18910             'IMG { cursor: pointer } ' +
18911         '</style>';
18912
18913         
18914         return '<html><head>' + st  +
18915             //<style type="text/css">' +
18916             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18917             //'</style>' +
18918             ' </head><body class="roo-htmleditor-body"></body></html>';
18919     },
18920
18921     // private
18922     onRender : function(ct, position)
18923     {
18924         var _t = this;
18925         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18926         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18927         
18928         
18929         this.el.dom.style.border = '0 none';
18930         this.el.dom.setAttribute('tabIndex', -1);
18931         this.el.addClass('x-hidden hide');
18932         
18933         
18934         
18935         if(Roo.isIE){ // fix IE 1px bogus margin
18936             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18937         }
18938        
18939         
18940         this.frameId = Roo.id();
18941         
18942          
18943         
18944         var iframe = this.owner.wrap.createChild({
18945             tag: 'iframe',
18946             cls: 'form-control', // bootstrap..
18947             id: this.frameId,
18948             name: this.frameId,
18949             frameBorder : 'no',
18950             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18951         }, this.el
18952         );
18953         
18954         
18955         this.iframe = iframe.dom;
18956
18957          this.assignDocWin();
18958         
18959         this.doc.designMode = 'on';
18960        
18961         this.doc.open();
18962         this.doc.write(this.getDocMarkup());
18963         this.doc.close();
18964
18965         
18966         var task = { // must defer to wait for browser to be ready
18967             run : function(){
18968                 //console.log("run task?" + this.doc.readyState);
18969                 this.assignDocWin();
18970                 if(this.doc.body || this.doc.readyState == 'complete'){
18971                     try {
18972                         this.doc.designMode="on";
18973                     } catch (e) {
18974                         return;
18975                     }
18976                     Roo.TaskMgr.stop(task);
18977                     this.initEditor.defer(10, this);
18978                 }
18979             },
18980             interval : 10,
18981             duration: 10000,
18982             scope: this
18983         };
18984         Roo.TaskMgr.start(task);
18985
18986     },
18987
18988     // private
18989     onResize : function(w, h)
18990     {
18991          Roo.log('resize: ' +w + ',' + h );
18992         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18993         if(!this.iframe){
18994             return;
18995         }
18996         if(typeof w == 'number'){
18997             
18998             this.iframe.style.width = w + 'px';
18999         }
19000         if(typeof h == 'number'){
19001             
19002             this.iframe.style.height = h + 'px';
19003             if(this.doc){
19004                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19005             }
19006         }
19007         
19008     },
19009
19010     /**
19011      * Toggles the editor between standard and source edit mode.
19012      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19013      */
19014     toggleSourceEdit : function(sourceEditMode){
19015         
19016         this.sourceEditMode = sourceEditMode === true;
19017         
19018         if(this.sourceEditMode){
19019  
19020             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19021             
19022         }else{
19023             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19024             //this.iframe.className = '';
19025             this.deferFocus();
19026         }
19027         //this.setSize(this.owner.wrap.getSize());
19028         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19029     },
19030
19031     
19032   
19033
19034     /**
19035      * Protected method that will not generally be called directly. If you need/want
19036      * custom HTML cleanup, this is the method you should override.
19037      * @param {String} html The HTML to be cleaned
19038      * return {String} The cleaned HTML
19039      */
19040     cleanHtml : function(html){
19041         html = String(html);
19042         if(html.length > 5){
19043             if(Roo.isSafari){ // strip safari nonsense
19044                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19045             }
19046         }
19047         if(html == '&nbsp;'){
19048             html = '';
19049         }
19050         return html;
19051     },
19052
19053     /**
19054      * HTML Editor -> Textarea
19055      * Protected method that will not generally be called directly. Syncs the contents
19056      * of the editor iframe with the textarea.
19057      */
19058     syncValue : function(){
19059         if(this.initialized){
19060             var bd = (this.doc.body || this.doc.documentElement);
19061             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19062             var html = bd.innerHTML;
19063             if(Roo.isSafari){
19064                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19065                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19066                 if(m && m[1]){
19067                     html = '<div style="'+m[0]+'">' + html + '</div>';
19068                 }
19069             }
19070             html = this.cleanHtml(html);
19071             // fix up the special chars.. normaly like back quotes in word...
19072             // however we do not want to do this with chinese..
19073             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19074                 var cc = b.charCodeAt();
19075                 if (
19076                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19077                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19078                     (cc >= 0xf900 && cc < 0xfb00 )
19079                 ) {
19080                         return b;
19081                 }
19082                 return "&#"+cc+";" 
19083             });
19084             if(this.owner.fireEvent('beforesync', this, html) !== false){
19085                 this.el.dom.value = html;
19086                 this.owner.fireEvent('sync', this, html);
19087             }
19088         }
19089     },
19090
19091     /**
19092      * Protected method that will not generally be called directly. Pushes the value of the textarea
19093      * into the iframe editor.
19094      */
19095     pushValue : function(){
19096         if(this.initialized){
19097             var v = this.el.dom.value.trim();
19098             
19099 //            if(v.length < 1){
19100 //                v = '&#160;';
19101 //            }
19102             
19103             if(this.owner.fireEvent('beforepush', this, v) !== false){
19104                 var d = (this.doc.body || this.doc.documentElement);
19105                 d.innerHTML = v;
19106                 this.cleanUpPaste();
19107                 this.el.dom.value = d.innerHTML;
19108                 this.owner.fireEvent('push', this, v);
19109             }
19110         }
19111     },
19112
19113     // private
19114     deferFocus : function(){
19115         this.focus.defer(10, this);
19116     },
19117
19118     // doc'ed in Field
19119     focus : function(){
19120         if(this.win && !this.sourceEditMode){
19121             this.win.focus();
19122         }else{
19123             this.el.focus();
19124         }
19125     },
19126     
19127     assignDocWin: function()
19128     {
19129         var iframe = this.iframe;
19130         
19131          if(Roo.isIE){
19132             this.doc = iframe.contentWindow.document;
19133             this.win = iframe.contentWindow;
19134         } else {
19135 //            if (!Roo.get(this.frameId)) {
19136 //                return;
19137 //            }
19138 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19139 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19140             
19141             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19142                 return;
19143             }
19144             
19145             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19146             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19147         }
19148     },
19149     
19150     // private
19151     initEditor : function(){
19152         //console.log("INIT EDITOR");
19153         this.assignDocWin();
19154         
19155         
19156         
19157         this.doc.designMode="on";
19158         this.doc.open();
19159         this.doc.write(this.getDocMarkup());
19160         this.doc.close();
19161         
19162         var dbody = (this.doc.body || this.doc.documentElement);
19163         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19164         // this copies styles from the containing element into thsi one..
19165         // not sure why we need all of this..
19166         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19167         
19168         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19169         //ss['background-attachment'] = 'fixed'; // w3c
19170         dbody.bgProperties = 'fixed'; // ie
19171         //Roo.DomHelper.applyStyles(dbody, ss);
19172         Roo.EventManager.on(this.doc, {
19173             //'mousedown': this.onEditorEvent,
19174             'mouseup': this.onEditorEvent,
19175             'dblclick': this.onEditorEvent,
19176             'click': this.onEditorEvent,
19177             'keyup': this.onEditorEvent,
19178             buffer:100,
19179             scope: this
19180         });
19181         if(Roo.isGecko){
19182             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19183         }
19184         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19185             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19186         }
19187         this.initialized = true;
19188
19189         this.owner.fireEvent('initialize', this);
19190         this.pushValue();
19191     },
19192
19193     // private
19194     onDestroy : function(){
19195         
19196         
19197         
19198         if(this.rendered){
19199             
19200             //for (var i =0; i < this.toolbars.length;i++) {
19201             //    // fixme - ask toolbars for heights?
19202             //    this.toolbars[i].onDestroy();
19203            // }
19204             
19205             //this.wrap.dom.innerHTML = '';
19206             //this.wrap.remove();
19207         }
19208     },
19209
19210     // private
19211     onFirstFocus : function(){
19212         
19213         this.assignDocWin();
19214         
19215         
19216         this.activated = true;
19217          
19218     
19219         if(Roo.isGecko){ // prevent silly gecko errors
19220             this.win.focus();
19221             var s = this.win.getSelection();
19222             if(!s.focusNode || s.focusNode.nodeType != 3){
19223                 var r = s.getRangeAt(0);
19224                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19225                 r.collapse(true);
19226                 this.deferFocus();
19227             }
19228             try{
19229                 this.execCmd('useCSS', true);
19230                 this.execCmd('styleWithCSS', false);
19231             }catch(e){}
19232         }
19233         this.owner.fireEvent('activate', this);
19234     },
19235
19236     // private
19237     adjustFont: function(btn){
19238         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19239         //if(Roo.isSafari){ // safari
19240         //    adjust *= 2;
19241        // }
19242         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19243         if(Roo.isSafari){ // safari
19244             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19245             v =  (v < 10) ? 10 : v;
19246             v =  (v > 48) ? 48 : v;
19247             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19248             
19249         }
19250         
19251         
19252         v = Math.max(1, v+adjust);
19253         
19254         this.execCmd('FontSize', v  );
19255     },
19256
19257     onEditorEvent : function(e)
19258     {
19259         this.owner.fireEvent('editorevent', this, e);
19260       //  this.updateToolbar();
19261         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19262     },
19263
19264     insertTag : function(tg)
19265     {
19266         // could be a bit smarter... -> wrap the current selected tRoo..
19267         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19268             
19269             range = this.createRange(this.getSelection());
19270             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19271             wrappingNode.appendChild(range.extractContents());
19272             range.insertNode(wrappingNode);
19273
19274             return;
19275             
19276             
19277             
19278         }
19279         this.execCmd("formatblock",   tg);
19280         
19281     },
19282     
19283     insertText : function(txt)
19284     {
19285         
19286         
19287         var range = this.createRange();
19288         range.deleteContents();
19289                //alert(Sender.getAttribute('label'));
19290                
19291         range.insertNode(this.doc.createTextNode(txt));
19292     } ,
19293     
19294      
19295
19296     /**
19297      * Executes a Midas editor command on the editor document and performs necessary focus and
19298      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19299      * @param {String} cmd The Midas command
19300      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19301      */
19302     relayCmd : function(cmd, value){
19303         this.win.focus();
19304         this.execCmd(cmd, value);
19305         this.owner.fireEvent('editorevent', this);
19306         //this.updateToolbar();
19307         this.owner.deferFocus();
19308     },
19309
19310     /**
19311      * Executes a Midas editor command directly on the editor document.
19312      * For visual commands, you should use {@link #relayCmd} instead.
19313      * <b>This should only be called after the editor is initialized.</b>
19314      * @param {String} cmd The Midas command
19315      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19316      */
19317     execCmd : function(cmd, value){
19318         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19319         this.syncValue();
19320     },
19321  
19322  
19323    
19324     /**
19325      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19326      * to insert tRoo.
19327      * @param {String} text | dom node.. 
19328      */
19329     insertAtCursor : function(text)
19330     {
19331         
19332         
19333         
19334         if(!this.activated){
19335             return;
19336         }
19337         /*
19338         if(Roo.isIE){
19339             this.win.focus();
19340             var r = this.doc.selection.createRange();
19341             if(r){
19342                 r.collapse(true);
19343                 r.pasteHTML(text);
19344                 this.syncValue();
19345                 this.deferFocus();
19346             
19347             }
19348             return;
19349         }
19350         */
19351         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19352             this.win.focus();
19353             
19354             
19355             // from jquery ui (MIT licenced)
19356             var range, node;
19357             var win = this.win;
19358             
19359             if (win.getSelection && win.getSelection().getRangeAt) {
19360                 range = win.getSelection().getRangeAt(0);
19361                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19362                 range.insertNode(node);
19363             } else if (win.document.selection && win.document.selection.createRange) {
19364                 // no firefox support
19365                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19366                 win.document.selection.createRange().pasteHTML(txt);
19367             } else {
19368                 // no firefox support
19369                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19370                 this.execCmd('InsertHTML', txt);
19371             } 
19372             
19373             this.syncValue();
19374             
19375             this.deferFocus();
19376         }
19377     },
19378  // private
19379     mozKeyPress : function(e){
19380         if(e.ctrlKey){
19381             var c = e.getCharCode(), cmd;
19382           
19383             if(c > 0){
19384                 c = String.fromCharCode(c).toLowerCase();
19385                 switch(c){
19386                     case 'b':
19387                         cmd = 'bold';
19388                         break;
19389                     case 'i':
19390                         cmd = 'italic';
19391                         break;
19392                     
19393                     case 'u':
19394                         cmd = 'underline';
19395                         break;
19396                     
19397                     case 'v':
19398                         this.cleanUpPaste.defer(100, this);
19399                         return;
19400                         
19401                 }
19402                 if(cmd){
19403                     this.win.focus();
19404                     this.execCmd(cmd);
19405                     this.deferFocus();
19406                     e.preventDefault();
19407                 }
19408                 
19409             }
19410         }
19411     },
19412
19413     // private
19414     fixKeys : function(){ // load time branching for fastest keydown performance
19415         if(Roo.isIE){
19416             return function(e){
19417                 var k = e.getKey(), r;
19418                 if(k == e.TAB){
19419                     e.stopEvent();
19420                     r = this.doc.selection.createRange();
19421                     if(r){
19422                         r.collapse(true);
19423                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19424                         this.deferFocus();
19425                     }
19426                     return;
19427                 }
19428                 
19429                 if(k == e.ENTER){
19430                     r = this.doc.selection.createRange();
19431                     if(r){
19432                         var target = r.parentElement();
19433                         if(!target || target.tagName.toLowerCase() != 'li'){
19434                             e.stopEvent();
19435                             r.pasteHTML('<br />');
19436                             r.collapse(false);
19437                             r.select();
19438                         }
19439                     }
19440                 }
19441                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19442                     this.cleanUpPaste.defer(100, this);
19443                     return;
19444                 }
19445                 
19446                 
19447             };
19448         }else if(Roo.isOpera){
19449             return function(e){
19450                 var k = e.getKey();
19451                 if(k == e.TAB){
19452                     e.stopEvent();
19453                     this.win.focus();
19454                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19455                     this.deferFocus();
19456                 }
19457                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19458                     this.cleanUpPaste.defer(100, this);
19459                     return;
19460                 }
19461                 
19462             };
19463         }else if(Roo.isSafari){
19464             return function(e){
19465                 var k = e.getKey();
19466                 
19467                 if(k == e.TAB){
19468                     e.stopEvent();
19469                     this.execCmd('InsertText','\t');
19470                     this.deferFocus();
19471                     return;
19472                 }
19473                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19474                     this.cleanUpPaste.defer(100, this);
19475                     return;
19476                 }
19477                 
19478              };
19479         }
19480     }(),
19481     
19482     getAllAncestors: function()
19483     {
19484         var p = this.getSelectedNode();
19485         var a = [];
19486         if (!p) {
19487             a.push(p); // push blank onto stack..
19488             p = this.getParentElement();
19489         }
19490         
19491         
19492         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19493             a.push(p);
19494             p = p.parentNode;
19495         }
19496         a.push(this.doc.body);
19497         return a;
19498     },
19499     lastSel : false,
19500     lastSelNode : false,
19501     
19502     
19503     getSelection : function() 
19504     {
19505         this.assignDocWin();
19506         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19507     },
19508     
19509     getSelectedNode: function() 
19510     {
19511         // this may only work on Gecko!!!
19512         
19513         // should we cache this!!!!
19514         
19515         
19516         
19517          
19518         var range = this.createRange(this.getSelection()).cloneRange();
19519         
19520         if (Roo.isIE) {
19521             var parent = range.parentElement();
19522             while (true) {
19523                 var testRange = range.duplicate();
19524                 testRange.moveToElementText(parent);
19525                 if (testRange.inRange(range)) {
19526                     break;
19527                 }
19528                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19529                     break;
19530                 }
19531                 parent = parent.parentElement;
19532             }
19533             return parent;
19534         }
19535         
19536         // is ancestor a text element.
19537         var ac =  range.commonAncestorContainer;
19538         if (ac.nodeType == 3) {
19539             ac = ac.parentNode;
19540         }
19541         
19542         var ar = ac.childNodes;
19543          
19544         var nodes = [];
19545         var other_nodes = [];
19546         var has_other_nodes = false;
19547         for (var i=0;i<ar.length;i++) {
19548             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19549                 continue;
19550             }
19551             // fullly contained node.
19552             
19553             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19554                 nodes.push(ar[i]);
19555                 continue;
19556             }
19557             
19558             // probably selected..
19559             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19560                 other_nodes.push(ar[i]);
19561                 continue;
19562             }
19563             // outer..
19564             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19565                 continue;
19566             }
19567             
19568             
19569             has_other_nodes = true;
19570         }
19571         if (!nodes.length && other_nodes.length) {
19572             nodes= other_nodes;
19573         }
19574         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19575             return false;
19576         }
19577         
19578         return nodes[0];
19579     },
19580     createRange: function(sel)
19581     {
19582         // this has strange effects when using with 
19583         // top toolbar - not sure if it's a great idea.
19584         //this.editor.contentWindow.focus();
19585         if (typeof sel != "undefined") {
19586             try {
19587                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19588             } catch(e) {
19589                 return this.doc.createRange();
19590             }
19591         } else {
19592             return this.doc.createRange();
19593         }
19594     },
19595     getParentElement: function()
19596     {
19597         
19598         this.assignDocWin();
19599         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19600         
19601         var range = this.createRange(sel);
19602          
19603         try {
19604             var p = range.commonAncestorContainer;
19605             while (p.nodeType == 3) { // text node
19606                 p = p.parentNode;
19607             }
19608             return p;
19609         } catch (e) {
19610             return null;
19611         }
19612     
19613     },
19614     /***
19615      *
19616      * Range intersection.. the hard stuff...
19617      *  '-1' = before
19618      *  '0' = hits..
19619      *  '1' = after.
19620      *         [ -- selected range --- ]
19621      *   [fail]                        [fail]
19622      *
19623      *    basically..
19624      *      if end is before start or  hits it. fail.
19625      *      if start is after end or hits it fail.
19626      *
19627      *   if either hits (but other is outside. - then it's not 
19628      *   
19629      *    
19630      **/
19631     
19632     
19633     // @see http://www.thismuchiknow.co.uk/?p=64.
19634     rangeIntersectsNode : function(range, node)
19635     {
19636         var nodeRange = node.ownerDocument.createRange();
19637         try {
19638             nodeRange.selectNode(node);
19639         } catch (e) {
19640             nodeRange.selectNodeContents(node);
19641         }
19642     
19643         var rangeStartRange = range.cloneRange();
19644         rangeStartRange.collapse(true);
19645     
19646         var rangeEndRange = range.cloneRange();
19647         rangeEndRange.collapse(false);
19648     
19649         var nodeStartRange = nodeRange.cloneRange();
19650         nodeStartRange.collapse(true);
19651     
19652         var nodeEndRange = nodeRange.cloneRange();
19653         nodeEndRange.collapse(false);
19654     
19655         return rangeStartRange.compareBoundaryPoints(
19656                  Range.START_TO_START, nodeEndRange) == -1 &&
19657                rangeEndRange.compareBoundaryPoints(
19658                  Range.START_TO_START, nodeStartRange) == 1;
19659         
19660          
19661     },
19662     rangeCompareNode : function(range, node)
19663     {
19664         var nodeRange = node.ownerDocument.createRange();
19665         try {
19666             nodeRange.selectNode(node);
19667         } catch (e) {
19668             nodeRange.selectNodeContents(node);
19669         }
19670         
19671         
19672         range.collapse(true);
19673     
19674         nodeRange.collapse(true);
19675      
19676         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19677         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19678          
19679         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19680         
19681         var nodeIsBefore   =  ss == 1;
19682         var nodeIsAfter    = ee == -1;
19683         
19684         if (nodeIsBefore && nodeIsAfter)
19685             return 0; // outer
19686         if (!nodeIsBefore && nodeIsAfter)
19687             return 1; //right trailed.
19688         
19689         if (nodeIsBefore && !nodeIsAfter)
19690             return 2;  // left trailed.
19691         // fully contined.
19692         return 3;
19693     },
19694
19695     // private? - in a new class?
19696     cleanUpPaste :  function()
19697     {
19698         // cleans up the whole document..
19699         Roo.log('cleanuppaste');
19700         
19701         this.cleanUpChildren(this.doc.body);
19702         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19703         if (clean != this.doc.body.innerHTML) {
19704             this.doc.body.innerHTML = clean;
19705         }
19706         
19707     },
19708     
19709     cleanWordChars : function(input) {// change the chars to hex code
19710         var he = Roo.HtmlEditorCore;
19711         
19712         var output = input;
19713         Roo.each(he.swapCodes, function(sw) { 
19714             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19715             
19716             output = output.replace(swapper, sw[1]);
19717         });
19718         
19719         return output;
19720     },
19721     
19722     
19723     cleanUpChildren : function (n)
19724     {
19725         if (!n.childNodes.length) {
19726             return;
19727         }
19728         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19729            this.cleanUpChild(n.childNodes[i]);
19730         }
19731     },
19732     
19733     
19734         
19735     
19736     cleanUpChild : function (node)
19737     {
19738         var ed = this;
19739         //console.log(node);
19740         if (node.nodeName == "#text") {
19741             // clean up silly Windows -- stuff?
19742             return; 
19743         }
19744         if (node.nodeName == "#comment") {
19745             node.parentNode.removeChild(node);
19746             // clean up silly Windows -- stuff?
19747             return; 
19748         }
19749         var lcname = node.tagName.toLowerCase();
19750         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19751         // whitelist of tags..
19752         
19753         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19754             // remove node.
19755             node.parentNode.removeChild(node);
19756             return;
19757             
19758         }
19759         
19760         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19761         
19762         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19763         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19764         
19765         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19766         //    remove_keep_children = true;
19767         //}
19768         
19769         if (remove_keep_children) {
19770             this.cleanUpChildren(node);
19771             // inserts everything just before this node...
19772             while (node.childNodes.length) {
19773                 var cn = node.childNodes[0];
19774                 node.removeChild(cn);
19775                 node.parentNode.insertBefore(cn, node);
19776             }
19777             node.parentNode.removeChild(node);
19778             return;
19779         }
19780         
19781         if (!node.attributes || !node.attributes.length) {
19782             this.cleanUpChildren(node);
19783             return;
19784         }
19785         
19786         function cleanAttr(n,v)
19787         {
19788             
19789             if (v.match(/^\./) || v.match(/^\//)) {
19790                 return;
19791             }
19792             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19793                 return;
19794             }
19795             if (v.match(/^#/)) {
19796                 return;
19797             }
19798 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19799             node.removeAttribute(n);
19800             
19801         }
19802         
19803         var cwhite = this.cwhite;
19804         var cblack = this.cblack;
19805             
19806         function cleanStyle(n,v)
19807         {
19808             if (v.match(/expression/)) { //XSS?? should we even bother..
19809                 node.removeAttribute(n);
19810                 return;
19811             }
19812             
19813             var parts = v.split(/;/);
19814             var clean = [];
19815             
19816             Roo.each(parts, function(p) {
19817                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19818                 if (!p.length) {
19819                     return true;
19820                 }
19821                 var l = p.split(':').shift().replace(/\s+/g,'');
19822                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19823                 
19824                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19825 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19826                     //node.removeAttribute(n);
19827                     return true;
19828                 }
19829                 //Roo.log()
19830                 // only allow 'c whitelisted system attributes'
19831                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19832 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19833                     //node.removeAttribute(n);
19834                     return true;
19835                 }
19836                 
19837                 
19838                  
19839                 
19840                 clean.push(p);
19841                 return true;
19842             });
19843             if (clean.length) { 
19844                 node.setAttribute(n, clean.join(';'));
19845             } else {
19846                 node.removeAttribute(n);
19847             }
19848             
19849         }
19850         
19851         
19852         for (var i = node.attributes.length-1; i > -1 ; i--) {
19853             var a = node.attributes[i];
19854             //console.log(a);
19855             
19856             if (a.name.toLowerCase().substr(0,2)=='on')  {
19857                 node.removeAttribute(a.name);
19858                 continue;
19859             }
19860             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19861                 node.removeAttribute(a.name);
19862                 continue;
19863             }
19864             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19865                 cleanAttr(a.name,a.value); // fixme..
19866                 continue;
19867             }
19868             if (a.name == 'style') {
19869                 cleanStyle(a.name,a.value);
19870                 continue;
19871             }
19872             /// clean up MS crap..
19873             // tecnically this should be a list of valid class'es..
19874             
19875             
19876             if (a.name == 'class') {
19877                 if (a.value.match(/^Mso/)) {
19878                     node.className = '';
19879                 }
19880                 
19881                 if (a.value.match(/body/)) {
19882                     node.className = '';
19883                 }
19884                 continue;
19885             }
19886             
19887             // style cleanup!?
19888             // class cleanup?
19889             
19890         }
19891         
19892         
19893         this.cleanUpChildren(node);
19894         
19895         
19896     },
19897     
19898     /**
19899      * Clean up MS wordisms...
19900      */
19901     cleanWord : function(node)
19902     {
19903         
19904         
19905         if (!node) {
19906             this.cleanWord(this.doc.body);
19907             return;
19908         }
19909         if (node.nodeName == "#text") {
19910             // clean up silly Windows -- stuff?
19911             return; 
19912         }
19913         if (node.nodeName == "#comment") {
19914             node.parentNode.removeChild(node);
19915             // clean up silly Windows -- stuff?
19916             return; 
19917         }
19918         
19919         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19920             node.parentNode.removeChild(node);
19921             return;
19922         }
19923         
19924         // remove - but keep children..
19925         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19926             while (node.childNodes.length) {
19927                 var cn = node.childNodes[0];
19928                 node.removeChild(cn);
19929                 node.parentNode.insertBefore(cn, node);
19930             }
19931             node.parentNode.removeChild(node);
19932             this.iterateChildren(node, this.cleanWord);
19933             return;
19934         }
19935         // clean styles
19936         if (node.className.length) {
19937             
19938             var cn = node.className.split(/\W+/);
19939             var cna = [];
19940             Roo.each(cn, function(cls) {
19941                 if (cls.match(/Mso[a-zA-Z]+/)) {
19942                     return;
19943                 }
19944                 cna.push(cls);
19945             });
19946             node.className = cna.length ? cna.join(' ') : '';
19947             if (!cna.length) {
19948                 node.removeAttribute("class");
19949             }
19950         }
19951         
19952         if (node.hasAttribute("lang")) {
19953             node.removeAttribute("lang");
19954         }
19955         
19956         if (node.hasAttribute("style")) {
19957             
19958             var styles = node.getAttribute("style").split(";");
19959             var nstyle = [];
19960             Roo.each(styles, function(s) {
19961                 if (!s.match(/:/)) {
19962                     return;
19963                 }
19964                 var kv = s.split(":");
19965                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19966                     return;
19967                 }
19968                 // what ever is left... we allow.
19969                 nstyle.push(s);
19970             });
19971             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19972             if (!nstyle.length) {
19973                 node.removeAttribute('style');
19974             }
19975         }
19976         this.iterateChildren(node, this.cleanWord);
19977         
19978         
19979         
19980     },
19981     /**
19982      * iterateChildren of a Node, calling fn each time, using this as the scole..
19983      * @param {DomNode} node node to iterate children of.
19984      * @param {Function} fn method of this class to call on each item.
19985      */
19986     iterateChildren : function(node, fn)
19987     {
19988         if (!node.childNodes.length) {
19989                 return;
19990         }
19991         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19992            fn.call(this, node.childNodes[i])
19993         }
19994     },
19995     
19996     
19997     /**
19998      * cleanTableWidths.
19999      *
20000      * Quite often pasting from word etc.. results in tables with column and widths.
20001      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20002      *
20003      */
20004     cleanTableWidths : function(node)
20005     {
20006          
20007          
20008         if (!node) {
20009             this.cleanTableWidths(this.doc.body);
20010             return;
20011         }
20012         
20013         // ignore list...
20014         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20015             return; 
20016         }
20017         Roo.log(node.tagName);
20018         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20019             this.iterateChildren(node, this.cleanTableWidths);
20020             return;
20021         }
20022         if (node.hasAttribute('width')) {
20023             node.removeAttribute('width');
20024         }
20025         
20026          
20027         if (node.hasAttribute("style")) {
20028             // pretty basic...
20029             
20030             var styles = node.getAttribute("style").split(";");
20031             var nstyle = [];
20032             Roo.each(styles, function(s) {
20033                 if (!s.match(/:/)) {
20034                     return;
20035                 }
20036                 var kv = s.split(":");
20037                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20038                     return;
20039                 }
20040                 // what ever is left... we allow.
20041                 nstyle.push(s);
20042             });
20043             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20044             if (!nstyle.length) {
20045                 node.removeAttribute('style');
20046             }
20047         }
20048         
20049         this.iterateChildren(node, this.cleanTableWidths);
20050         
20051         
20052     },
20053     
20054     
20055     
20056     
20057     domToHTML : function(currentElement, depth, nopadtext) {
20058         
20059         depth = depth || 0;
20060         nopadtext = nopadtext || false;
20061     
20062         if (!currentElement) {
20063             return this.domToHTML(this.doc.body);
20064         }
20065         
20066         //Roo.log(currentElement);
20067         var j;
20068         var allText = false;
20069         var nodeName = currentElement.nodeName;
20070         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20071         
20072         if  (nodeName == '#text') {
20073             
20074             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20075         }
20076         
20077         
20078         var ret = '';
20079         if (nodeName != 'BODY') {
20080              
20081             var i = 0;
20082             // Prints the node tagName, such as <A>, <IMG>, etc
20083             if (tagName) {
20084                 var attr = [];
20085                 for(i = 0; i < currentElement.attributes.length;i++) {
20086                     // quoting?
20087                     var aname = currentElement.attributes.item(i).name;
20088                     if (!currentElement.attributes.item(i).value.length) {
20089                         continue;
20090                     }
20091                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20092                 }
20093                 
20094                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20095             } 
20096             else {
20097                 
20098                 // eack
20099             }
20100         } else {
20101             tagName = false;
20102         }
20103         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20104             return ret;
20105         }
20106         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20107             nopadtext = true;
20108         }
20109         
20110         
20111         // Traverse the tree
20112         i = 0;
20113         var currentElementChild = currentElement.childNodes.item(i);
20114         var allText = true;
20115         var innerHTML  = '';
20116         lastnode = '';
20117         while (currentElementChild) {
20118             // Formatting code (indent the tree so it looks nice on the screen)
20119             var nopad = nopadtext;
20120             if (lastnode == 'SPAN') {
20121                 nopad  = true;
20122             }
20123             // text
20124             if  (currentElementChild.nodeName == '#text') {
20125                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20126                 toadd = nopadtext ? toadd : toadd.trim();
20127                 if (!nopad && toadd.length > 80) {
20128                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20129                 }
20130                 innerHTML  += toadd;
20131                 
20132                 i++;
20133                 currentElementChild = currentElement.childNodes.item(i);
20134                 lastNode = '';
20135                 continue;
20136             }
20137             allText = false;
20138             
20139             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20140                 
20141             // Recursively traverse the tree structure of the child node
20142             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20143             lastnode = currentElementChild.nodeName;
20144             i++;
20145             currentElementChild=currentElement.childNodes.item(i);
20146         }
20147         
20148         ret += innerHTML;
20149         
20150         if (!allText) {
20151                 // The remaining code is mostly for formatting the tree
20152             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20153         }
20154         
20155         
20156         if (tagName) {
20157             ret+= "</"+tagName+">";
20158         }
20159         return ret;
20160         
20161     },
20162         
20163     applyBlacklists : function()
20164     {
20165         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20166         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20167         
20168         this.white = [];
20169         this.black = [];
20170         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20171             if (b.indexOf(tag) > -1) {
20172                 return;
20173             }
20174             this.white.push(tag);
20175             
20176         }, this);
20177         
20178         Roo.each(w, function(tag) {
20179             if (b.indexOf(tag) > -1) {
20180                 return;
20181             }
20182             if (this.white.indexOf(tag) > -1) {
20183                 return;
20184             }
20185             this.white.push(tag);
20186             
20187         }, this);
20188         
20189         
20190         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20191             if (w.indexOf(tag) > -1) {
20192                 return;
20193             }
20194             this.black.push(tag);
20195             
20196         }, this);
20197         
20198         Roo.each(b, function(tag) {
20199             if (w.indexOf(tag) > -1) {
20200                 return;
20201             }
20202             if (this.black.indexOf(tag) > -1) {
20203                 return;
20204             }
20205             this.black.push(tag);
20206             
20207         }, this);
20208         
20209         
20210         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20211         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20212         
20213         this.cwhite = [];
20214         this.cblack = [];
20215         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20216             if (b.indexOf(tag) > -1) {
20217                 return;
20218             }
20219             this.cwhite.push(tag);
20220             
20221         }, this);
20222         
20223         Roo.each(w, function(tag) {
20224             if (b.indexOf(tag) > -1) {
20225                 return;
20226             }
20227             if (this.cwhite.indexOf(tag) > -1) {
20228                 return;
20229             }
20230             this.cwhite.push(tag);
20231             
20232         }, this);
20233         
20234         
20235         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20236             if (w.indexOf(tag) > -1) {
20237                 return;
20238             }
20239             this.cblack.push(tag);
20240             
20241         }, this);
20242         
20243         Roo.each(b, function(tag) {
20244             if (w.indexOf(tag) > -1) {
20245                 return;
20246             }
20247             if (this.cblack.indexOf(tag) > -1) {
20248                 return;
20249             }
20250             this.cblack.push(tag);
20251             
20252         }, this);
20253     },
20254     
20255     setStylesheets : function(stylesheets)
20256     {
20257         if(typeof(stylesheets) == 'string'){
20258             Roo.get(this.iframe.contentDocument.head).createChild({
20259                 tag : 'link',
20260                 rel : 'stylesheet',
20261                 type : 'text/css',
20262                 href : stylesheets
20263             });
20264             
20265             return;
20266         }
20267         var _this = this;
20268      
20269         Roo.each(stylesheets, function(s) {
20270             if(!s.length){
20271                 return;
20272             }
20273             
20274             Roo.get(_this.iframe.contentDocument.head).createChild({
20275                 tag : 'link',
20276                 rel : 'stylesheet',
20277                 type : 'text/css',
20278                 href : s
20279             });
20280         });
20281
20282         
20283     },
20284     
20285     removeStylesheets : function()
20286     {
20287         var _this = this;
20288         
20289         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20290             s.remove();
20291         });
20292     }
20293     
20294     // hide stuff that is not compatible
20295     /**
20296      * @event blur
20297      * @hide
20298      */
20299     /**
20300      * @event change
20301      * @hide
20302      */
20303     /**
20304      * @event focus
20305      * @hide
20306      */
20307     /**
20308      * @event specialkey
20309      * @hide
20310      */
20311     /**
20312      * @cfg {String} fieldClass @hide
20313      */
20314     /**
20315      * @cfg {String} focusClass @hide
20316      */
20317     /**
20318      * @cfg {String} autoCreate @hide
20319      */
20320     /**
20321      * @cfg {String} inputType @hide
20322      */
20323     /**
20324      * @cfg {String} invalidClass @hide
20325      */
20326     /**
20327      * @cfg {String} invalidText @hide
20328      */
20329     /**
20330      * @cfg {String} msgFx @hide
20331      */
20332     /**
20333      * @cfg {String} validateOnBlur @hide
20334      */
20335 });
20336
20337 Roo.HtmlEditorCore.white = [
20338         'area', 'br', 'img', 'input', 'hr', 'wbr',
20339         
20340        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20341        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20342        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20343        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20344        'table',   'ul',         'xmp', 
20345        
20346        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20347       'thead',   'tr', 
20348      
20349       'dir', 'menu', 'ol', 'ul', 'dl',
20350        
20351       'embed',  'object'
20352 ];
20353
20354
20355 Roo.HtmlEditorCore.black = [
20356     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20357         'applet', // 
20358         'base',   'basefont', 'bgsound', 'blink',  'body', 
20359         'frame',  'frameset', 'head',    'html',   'ilayer', 
20360         'iframe', 'layer',  'link',     'meta',    'object',   
20361         'script', 'style' ,'title',  'xml' // clean later..
20362 ];
20363 Roo.HtmlEditorCore.clean = [
20364     'script', 'style', 'title', 'xml'
20365 ];
20366 Roo.HtmlEditorCore.remove = [
20367     'font'
20368 ];
20369 // attributes..
20370
20371 Roo.HtmlEditorCore.ablack = [
20372     'on'
20373 ];
20374     
20375 Roo.HtmlEditorCore.aclean = [ 
20376     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20377 ];
20378
20379 // protocols..
20380 Roo.HtmlEditorCore.pwhite= [
20381         'http',  'https',  'mailto'
20382 ];
20383
20384 // white listed style attributes.
20385 Roo.HtmlEditorCore.cwhite= [
20386       //  'text-align', /// default is to allow most things..
20387       
20388          
20389 //        'font-size'//??
20390 ];
20391
20392 // black listed style attributes.
20393 Roo.HtmlEditorCore.cblack= [
20394       //  'font-size' -- this can be set by the project 
20395 ];
20396
20397
20398 Roo.HtmlEditorCore.swapCodes   =[ 
20399     [    8211, "--" ], 
20400     [    8212, "--" ], 
20401     [    8216,  "'" ],  
20402     [    8217, "'" ],  
20403     [    8220, '"' ],  
20404     [    8221, '"' ],  
20405     [    8226, "*" ],  
20406     [    8230, "..." ]
20407 ]; 
20408
20409     /*
20410  * - LGPL
20411  *
20412  * HtmlEditor
20413  * 
20414  */
20415
20416 /**
20417  * @class Roo.bootstrap.HtmlEditor
20418  * @extends Roo.bootstrap.TextArea
20419  * Bootstrap HtmlEditor class
20420
20421  * @constructor
20422  * Create a new HtmlEditor
20423  * @param {Object} config The config object
20424  */
20425
20426 Roo.bootstrap.HtmlEditor = function(config){
20427     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20428     if (!this.toolbars) {
20429         this.toolbars = [];
20430     }
20431     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20432     this.addEvents({
20433             /**
20434              * @event initialize
20435              * Fires when the editor is fully initialized (including the iframe)
20436              * @param {HtmlEditor} this
20437              */
20438             initialize: true,
20439             /**
20440              * @event activate
20441              * Fires when the editor is first receives the focus. Any insertion must wait
20442              * until after this event.
20443              * @param {HtmlEditor} this
20444              */
20445             activate: true,
20446              /**
20447              * @event beforesync
20448              * Fires before the textarea is updated with content from the editor iframe. Return false
20449              * to cancel the sync.
20450              * @param {HtmlEditor} this
20451              * @param {String} html
20452              */
20453             beforesync: true,
20454              /**
20455              * @event beforepush
20456              * Fires before the iframe editor is updated with content from the textarea. Return false
20457              * to cancel the push.
20458              * @param {HtmlEditor} this
20459              * @param {String} html
20460              */
20461             beforepush: true,
20462              /**
20463              * @event sync
20464              * Fires when the textarea is updated with content from the editor iframe.
20465              * @param {HtmlEditor} this
20466              * @param {String} html
20467              */
20468             sync: true,
20469              /**
20470              * @event push
20471              * Fires when the iframe editor is updated with content from the textarea.
20472              * @param {HtmlEditor} this
20473              * @param {String} html
20474              */
20475             push: true,
20476              /**
20477              * @event editmodechange
20478              * Fires when the editor switches edit modes
20479              * @param {HtmlEditor} this
20480              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20481              */
20482             editmodechange: true,
20483             /**
20484              * @event editorevent
20485              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20486              * @param {HtmlEditor} this
20487              */
20488             editorevent: true,
20489             /**
20490              * @event firstfocus
20491              * Fires when on first focus - needed by toolbars..
20492              * @param {HtmlEditor} this
20493              */
20494             firstfocus: true,
20495             /**
20496              * @event autosave
20497              * Auto save the htmlEditor value as a file into Events
20498              * @param {HtmlEditor} this
20499              */
20500             autosave: true,
20501             /**
20502              * @event savedpreview
20503              * preview the saved version of htmlEditor
20504              * @param {HtmlEditor} this
20505              */
20506             savedpreview: true
20507         });
20508 };
20509
20510
20511 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20512     
20513     
20514       /**
20515      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20516      */
20517     toolbars : false,
20518    
20519      /**
20520      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20521      *                        Roo.resizable.
20522      */
20523     resizable : false,
20524      /**
20525      * @cfg {Number} height (in pixels)
20526      */   
20527     height: 300,
20528    /**
20529      * @cfg {Number} width (in pixels)
20530      */   
20531     width: false,
20532     
20533     /**
20534      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20535      * 
20536      */
20537     stylesheets: false,
20538     
20539     // id of frame..
20540     frameId: false,
20541     
20542     // private properties
20543     validationEvent : false,
20544     deferHeight: true,
20545     initialized : false,
20546     activated : false,
20547     
20548     onFocus : Roo.emptyFn,
20549     iframePad:3,
20550     hideMode:'offsets',
20551     
20552     
20553     tbContainer : false,
20554     
20555     toolbarContainer :function() {
20556         return this.wrap.select('.x-html-editor-tb',true).first();
20557     },
20558
20559     /**
20560      * Protected method that will not generally be called directly. It
20561      * is called when the editor creates its toolbar. Override this method if you need to
20562      * add custom toolbar buttons.
20563      * @param {HtmlEditor} editor
20564      */
20565     createToolbar : function(){
20566         
20567         Roo.log("create toolbars");
20568         
20569         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20570         this.toolbars[0].render(this.toolbarContainer());
20571         
20572         return;
20573         
20574 //        if (!editor.toolbars || !editor.toolbars.length) {
20575 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20576 //        }
20577 //        
20578 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20579 //            editor.toolbars[i] = Roo.factory(
20580 //                    typeof(editor.toolbars[i]) == 'string' ?
20581 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20582 //                Roo.bootstrap.HtmlEditor);
20583 //            editor.toolbars[i].init(editor);
20584 //        }
20585     },
20586
20587      
20588     // private
20589     onRender : function(ct, position)
20590     {
20591        // Roo.log("Call onRender: " + this.xtype);
20592         var _t = this;
20593         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20594       
20595         this.wrap = this.inputEl().wrap({
20596             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20597         });
20598         
20599         this.editorcore.onRender(ct, position);
20600          
20601         if (this.resizable) {
20602             this.resizeEl = new Roo.Resizable(this.wrap, {
20603                 pinned : true,
20604                 wrap: true,
20605                 dynamic : true,
20606                 minHeight : this.height,
20607                 height: this.height,
20608                 handles : this.resizable,
20609                 width: this.width,
20610                 listeners : {
20611                     resize : function(r, w, h) {
20612                         _t.onResize(w,h); // -something
20613                     }
20614                 }
20615             });
20616             
20617         }
20618         this.createToolbar(this);
20619        
20620         
20621         if(!this.width && this.resizable){
20622             this.setSize(this.wrap.getSize());
20623         }
20624         if (this.resizeEl) {
20625             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20626             // should trigger onReize..
20627         }
20628         
20629     },
20630
20631     // private
20632     onResize : function(w, h)
20633     {
20634         Roo.log('resize: ' +w + ',' + h );
20635         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20636         var ew = false;
20637         var eh = false;
20638         
20639         if(this.inputEl() ){
20640             if(typeof w == 'number'){
20641                 var aw = w - this.wrap.getFrameWidth('lr');
20642                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20643                 ew = aw;
20644             }
20645             if(typeof h == 'number'){
20646                  var tbh = -11;  // fixme it needs to tool bar size!
20647                 for (var i =0; i < this.toolbars.length;i++) {
20648                     // fixme - ask toolbars for heights?
20649                     tbh += this.toolbars[i].el.getHeight();
20650                     //if (this.toolbars[i].footer) {
20651                     //    tbh += this.toolbars[i].footer.el.getHeight();
20652                     //}
20653                 }
20654               
20655                 
20656                 
20657                 
20658                 
20659                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20660                 ah -= 5; // knock a few pixes off for look..
20661                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20662                 var eh = ah;
20663             }
20664         }
20665         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20666         this.editorcore.onResize(ew,eh);
20667         
20668     },
20669
20670     /**
20671      * Toggles the editor between standard and source edit mode.
20672      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20673      */
20674     toggleSourceEdit : function(sourceEditMode)
20675     {
20676         this.editorcore.toggleSourceEdit(sourceEditMode);
20677         
20678         if(this.editorcore.sourceEditMode){
20679             Roo.log('editor - showing textarea');
20680             
20681 //            Roo.log('in');
20682 //            Roo.log(this.syncValue());
20683             this.syncValue();
20684             this.inputEl().removeClass(['hide', 'x-hidden']);
20685             this.inputEl().dom.removeAttribute('tabIndex');
20686             this.inputEl().focus();
20687         }else{
20688             Roo.log('editor - hiding textarea');
20689 //            Roo.log('out')
20690 //            Roo.log(this.pushValue()); 
20691             this.pushValue();
20692             
20693             this.inputEl().addClass(['hide', 'x-hidden']);
20694             this.inputEl().dom.setAttribute('tabIndex', -1);
20695             //this.deferFocus();
20696         }
20697          
20698         if(this.resizable){
20699             this.setSize(this.wrap.getSize());
20700         }
20701         
20702         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20703     },
20704  
20705     // private (for BoxComponent)
20706     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20707
20708     // private (for BoxComponent)
20709     getResizeEl : function(){
20710         return this.wrap;
20711     },
20712
20713     // private (for BoxComponent)
20714     getPositionEl : function(){
20715         return this.wrap;
20716     },
20717
20718     // private
20719     initEvents : function(){
20720         this.originalValue = this.getValue();
20721     },
20722
20723 //    /**
20724 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20725 //     * @method
20726 //     */
20727 //    markInvalid : Roo.emptyFn,
20728 //    /**
20729 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20730 //     * @method
20731 //     */
20732 //    clearInvalid : Roo.emptyFn,
20733
20734     setValue : function(v){
20735         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20736         this.editorcore.pushValue();
20737     },
20738
20739      
20740     // private
20741     deferFocus : function(){
20742         this.focus.defer(10, this);
20743     },
20744
20745     // doc'ed in Field
20746     focus : function(){
20747         this.editorcore.focus();
20748         
20749     },
20750       
20751
20752     // private
20753     onDestroy : function(){
20754         
20755         
20756         
20757         if(this.rendered){
20758             
20759             for (var i =0; i < this.toolbars.length;i++) {
20760                 // fixme - ask toolbars for heights?
20761                 this.toolbars[i].onDestroy();
20762             }
20763             
20764             this.wrap.dom.innerHTML = '';
20765             this.wrap.remove();
20766         }
20767     },
20768
20769     // private
20770     onFirstFocus : function(){
20771         //Roo.log("onFirstFocus");
20772         this.editorcore.onFirstFocus();
20773          for (var i =0; i < this.toolbars.length;i++) {
20774             this.toolbars[i].onFirstFocus();
20775         }
20776         
20777     },
20778     
20779     // private
20780     syncValue : function()
20781     {   
20782         this.editorcore.syncValue();
20783     },
20784     
20785     pushValue : function()
20786     {   
20787         this.editorcore.pushValue();
20788     }
20789      
20790     
20791     // hide stuff that is not compatible
20792     /**
20793      * @event blur
20794      * @hide
20795      */
20796     /**
20797      * @event change
20798      * @hide
20799      */
20800     /**
20801      * @event focus
20802      * @hide
20803      */
20804     /**
20805      * @event specialkey
20806      * @hide
20807      */
20808     /**
20809      * @cfg {String} fieldClass @hide
20810      */
20811     /**
20812      * @cfg {String} focusClass @hide
20813      */
20814     /**
20815      * @cfg {String} autoCreate @hide
20816      */
20817     /**
20818      * @cfg {String} inputType @hide
20819      */
20820     /**
20821      * @cfg {String} invalidClass @hide
20822      */
20823     /**
20824      * @cfg {String} invalidText @hide
20825      */
20826     /**
20827      * @cfg {String} msgFx @hide
20828      */
20829     /**
20830      * @cfg {String} validateOnBlur @hide
20831      */
20832 });
20833  
20834     
20835    
20836    
20837    
20838       
20839 Roo.namespace('Roo.bootstrap.htmleditor');
20840 /**
20841  * @class Roo.bootstrap.HtmlEditorToolbar1
20842  * Basic Toolbar
20843  * 
20844  * Usage:
20845  *
20846  new Roo.bootstrap.HtmlEditor({
20847     ....
20848     toolbars : [
20849         new Roo.bootstrap.HtmlEditorToolbar1({
20850             disable : { fonts: 1 , format: 1, ..., ... , ...],
20851             btns : [ .... ]
20852         })
20853     }
20854      
20855  * 
20856  * @cfg {Object} disable List of elements to disable..
20857  * @cfg {Array} btns List of additional buttons.
20858  * 
20859  * 
20860  * NEEDS Extra CSS? 
20861  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20862  */
20863  
20864 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20865 {
20866     
20867     Roo.apply(this, config);
20868     
20869     // default disabled, based on 'good practice'..
20870     this.disable = this.disable || {};
20871     Roo.applyIf(this.disable, {
20872         fontSize : true,
20873         colors : true,
20874         specialElements : true
20875     });
20876     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20877     
20878     this.editor = config.editor;
20879     this.editorcore = config.editor.editorcore;
20880     
20881     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20882     
20883     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20884     // dont call parent... till later.
20885 }
20886 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20887      
20888     bar : true,
20889     
20890     editor : false,
20891     editorcore : false,
20892     
20893     
20894     formats : [
20895         "p" ,  
20896         "h1","h2","h3","h4","h5","h6", 
20897         "pre", "code", 
20898         "abbr", "acronym", "address", "cite", "samp", "var",
20899         'div','span'
20900     ],
20901     
20902     onRender : function(ct, position)
20903     {
20904        // Roo.log("Call onRender: " + this.xtype);
20905         
20906        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20907        Roo.log(this.el);
20908        this.el.dom.style.marginBottom = '0';
20909        var _this = this;
20910        var editorcore = this.editorcore;
20911        var editor= this.editor;
20912        
20913        var children = [];
20914        var btn = function(id,cmd , toggle, handler){
20915        
20916             var  event = toggle ? 'toggle' : 'click';
20917        
20918             var a = {
20919                 size : 'sm',
20920                 xtype: 'Button',
20921                 xns: Roo.bootstrap,
20922                 glyphicon : id,
20923                 cmd : id || cmd,
20924                 enableToggle:toggle !== false,
20925                 //html : 'submit'
20926                 pressed : toggle ? false : null,
20927                 listeners : {}
20928             };
20929             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20930                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20931             };
20932             children.push(a);
20933             return a;
20934        }
20935         
20936         var style = {
20937                 xtype: 'Button',
20938                 size : 'sm',
20939                 xns: Roo.bootstrap,
20940                 glyphicon : 'font',
20941                 //html : 'submit'
20942                 menu : {
20943                     xtype: 'Menu',
20944                     xns: Roo.bootstrap,
20945                     items:  []
20946                 }
20947         };
20948         Roo.each(this.formats, function(f) {
20949             style.menu.items.push({
20950                 xtype :'MenuItem',
20951                 xns: Roo.bootstrap,
20952                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20953                 tagname : f,
20954                 listeners : {
20955                     click : function()
20956                     {
20957                         editorcore.insertTag(this.tagname);
20958                         editor.focus();
20959                     }
20960                 }
20961                 
20962             });
20963         });
20964          children.push(style);   
20965             
20966             
20967         btn('bold',false,true);
20968         btn('italic',false,true);
20969         btn('align-left', 'justifyleft',true);
20970         btn('align-center', 'justifycenter',true);
20971         btn('align-right' , 'justifyright',true);
20972         btn('link', false, false, function(btn) {
20973             //Roo.log("create link?");
20974             var url = prompt(this.createLinkText, this.defaultLinkValue);
20975             if(url && url != 'http:/'+'/'){
20976                 this.editorcore.relayCmd('createlink', url);
20977             }
20978         }),
20979         btn('list','insertunorderedlist',true);
20980         btn('pencil', false,true, function(btn){
20981                 Roo.log(this);
20982                 
20983                 this.toggleSourceEdit(btn.pressed);
20984         });
20985         /*
20986         var cog = {
20987                 xtype: 'Button',
20988                 size : 'sm',
20989                 xns: Roo.bootstrap,
20990                 glyphicon : 'cog',
20991                 //html : 'submit'
20992                 menu : {
20993                     xtype: 'Menu',
20994                     xns: Roo.bootstrap,
20995                     items:  []
20996                 }
20997         };
20998         
20999         cog.menu.items.push({
21000             xtype :'MenuItem',
21001             xns: Roo.bootstrap,
21002             html : Clean styles,
21003             tagname : f,
21004             listeners : {
21005                 click : function()
21006                 {
21007                     editorcore.insertTag(this.tagname);
21008                     editor.focus();
21009                 }
21010             }
21011             
21012         });
21013        */
21014         
21015          
21016        this.xtype = 'NavSimplebar';
21017         
21018         for(var i=0;i< children.length;i++) {
21019             
21020             this.buttons.add(this.addxtypeChild(children[i]));
21021             
21022         }
21023         
21024         editor.on('editorevent', this.updateToolbar, this);
21025     },
21026     onBtnClick : function(id)
21027     {
21028        this.editorcore.relayCmd(id);
21029        this.editorcore.focus();
21030     },
21031     
21032     /**
21033      * Protected method that will not generally be called directly. It triggers
21034      * a toolbar update by reading the markup state of the current selection in the editor.
21035      */
21036     updateToolbar: function(){
21037
21038         if(!this.editorcore.activated){
21039             this.editor.onFirstFocus(); // is this neeed?
21040             return;
21041         }
21042
21043         var btns = this.buttons; 
21044         var doc = this.editorcore.doc;
21045         btns.get('bold').setActive(doc.queryCommandState('bold'));
21046         btns.get('italic').setActive(doc.queryCommandState('italic'));
21047         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21048         
21049         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21050         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21051         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21052         
21053         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21054         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21055          /*
21056         
21057         var ans = this.editorcore.getAllAncestors();
21058         if (this.formatCombo) {
21059             
21060             
21061             var store = this.formatCombo.store;
21062             this.formatCombo.setValue("");
21063             for (var i =0; i < ans.length;i++) {
21064                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21065                     // select it..
21066                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21067                     break;
21068                 }
21069             }
21070         }
21071         
21072         
21073         
21074         // hides menus... - so this cant be on a menu...
21075         Roo.bootstrap.MenuMgr.hideAll();
21076         */
21077         Roo.bootstrap.MenuMgr.hideAll();
21078         //this.editorsyncValue();
21079     },
21080     onFirstFocus: function() {
21081         this.buttons.each(function(item){
21082            item.enable();
21083         });
21084     },
21085     toggleSourceEdit : function(sourceEditMode){
21086         
21087           
21088         if(sourceEditMode){
21089             Roo.log("disabling buttons");
21090            this.buttons.each( function(item){
21091                 if(item.cmd != 'pencil'){
21092                     item.disable();
21093                 }
21094             });
21095           
21096         }else{
21097             Roo.log("enabling buttons");
21098             if(this.editorcore.initialized){
21099                 this.buttons.each( function(item){
21100                     item.enable();
21101                 });
21102             }
21103             
21104         }
21105         Roo.log("calling toggole on editor");
21106         // tell the editor that it's been pressed..
21107         this.editor.toggleSourceEdit(sourceEditMode);
21108        
21109     }
21110 });
21111
21112
21113
21114
21115
21116 /**
21117  * @class Roo.bootstrap.Table.AbstractSelectionModel
21118  * @extends Roo.util.Observable
21119  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21120  * implemented by descendant classes.  This class should not be directly instantiated.
21121  * @constructor
21122  */
21123 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21124     this.locked = false;
21125     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21126 };
21127
21128
21129 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21130     /** @ignore Called by the grid automatically. Do not call directly. */
21131     init : function(grid){
21132         this.grid = grid;
21133         this.initEvents();
21134     },
21135
21136     /**
21137      * Locks the selections.
21138      */
21139     lock : function(){
21140         this.locked = true;
21141     },
21142
21143     /**
21144      * Unlocks the selections.
21145      */
21146     unlock : function(){
21147         this.locked = false;
21148     },
21149
21150     /**
21151      * Returns true if the selections are locked.
21152      * @return {Boolean}
21153      */
21154     isLocked : function(){
21155         return this.locked;
21156     }
21157 });
21158 /**
21159  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21160  * @class Roo.bootstrap.Table.RowSelectionModel
21161  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21162  * It supports multiple selections and keyboard selection/navigation. 
21163  * @constructor
21164  * @param {Object} config
21165  */
21166
21167 Roo.bootstrap.Table.RowSelectionModel = function(config){
21168     Roo.apply(this, config);
21169     this.selections = new Roo.util.MixedCollection(false, function(o){
21170         return o.id;
21171     });
21172
21173     this.last = false;
21174     this.lastActive = false;
21175
21176     this.addEvents({
21177         /**
21178              * @event selectionchange
21179              * Fires when the selection changes
21180              * @param {SelectionModel} this
21181              */
21182             "selectionchange" : true,
21183         /**
21184              * @event afterselectionchange
21185              * Fires after the selection changes (eg. by key press or clicking)
21186              * @param {SelectionModel} this
21187              */
21188             "afterselectionchange" : true,
21189         /**
21190              * @event beforerowselect
21191              * Fires when a row is selected being selected, return false to cancel.
21192              * @param {SelectionModel} this
21193              * @param {Number} rowIndex The selected index
21194              * @param {Boolean} keepExisting False if other selections will be cleared
21195              */
21196             "beforerowselect" : true,
21197         /**
21198              * @event rowselect
21199              * Fires when a row is selected.
21200              * @param {SelectionModel} this
21201              * @param {Number} rowIndex The selected index
21202              * @param {Roo.data.Record} r The record
21203              */
21204             "rowselect" : true,
21205         /**
21206              * @event rowdeselect
21207              * Fires when a row is deselected.
21208              * @param {SelectionModel} this
21209              * @param {Number} rowIndex The selected index
21210              */
21211         "rowdeselect" : true
21212     });
21213     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21214     this.locked = false;
21215 };
21216
21217 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21218     /**
21219      * @cfg {Boolean} singleSelect
21220      * True to allow selection of only one row at a time (defaults to false)
21221      */
21222     singleSelect : false,
21223
21224     // private
21225     initEvents : function(){
21226
21227         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21228             this.grid.on("mousedown", this.handleMouseDown, this);
21229         }else{ // allow click to work like normal
21230             this.grid.on("rowclick", this.handleDragableRowClick, this);
21231         }
21232
21233         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21234             "up" : function(e){
21235                 if(!e.shiftKey){
21236                     this.selectPrevious(e.shiftKey);
21237                 }else if(this.last !== false && this.lastActive !== false){
21238                     var last = this.last;
21239                     this.selectRange(this.last,  this.lastActive-1);
21240                     this.grid.getView().focusRow(this.lastActive);
21241                     if(last !== false){
21242                         this.last = last;
21243                     }
21244                 }else{
21245                     this.selectFirstRow();
21246                 }
21247                 this.fireEvent("afterselectionchange", this);
21248             },
21249             "down" : function(e){
21250                 if(!e.shiftKey){
21251                     this.selectNext(e.shiftKey);
21252                 }else if(this.last !== false && this.lastActive !== false){
21253                     var last = this.last;
21254                     this.selectRange(this.last,  this.lastActive+1);
21255                     this.grid.getView().focusRow(this.lastActive);
21256                     if(last !== false){
21257                         this.last = last;
21258                     }
21259                 }else{
21260                     this.selectFirstRow();
21261                 }
21262                 this.fireEvent("afterselectionchange", this);
21263             },
21264             scope: this
21265         });
21266
21267         var view = this.grid.view;
21268         view.on("refresh", this.onRefresh, this);
21269         view.on("rowupdated", this.onRowUpdated, this);
21270         view.on("rowremoved", this.onRemove, this);
21271     },
21272
21273     // private
21274     onRefresh : function(){
21275         var ds = this.grid.dataSource, i, v = this.grid.view;
21276         var s = this.selections;
21277         s.each(function(r){
21278             if((i = ds.indexOfId(r.id)) != -1){
21279                 v.onRowSelect(i);
21280             }else{
21281                 s.remove(r);
21282             }
21283         });
21284     },
21285
21286     // private
21287     onRemove : function(v, index, r){
21288         this.selections.remove(r);
21289     },
21290
21291     // private
21292     onRowUpdated : function(v, index, r){
21293         if(this.isSelected(r)){
21294             v.onRowSelect(index);
21295         }
21296     },
21297
21298     /**
21299      * Select records.
21300      * @param {Array} records The records to select
21301      * @param {Boolean} keepExisting (optional) True to keep existing selections
21302      */
21303     selectRecords : function(records, keepExisting){
21304         if(!keepExisting){
21305             this.clearSelections();
21306         }
21307         var ds = this.grid.dataSource;
21308         for(var i = 0, len = records.length; i < len; i++){
21309             this.selectRow(ds.indexOf(records[i]), true);
21310         }
21311     },
21312
21313     /**
21314      * Gets the number of selected rows.
21315      * @return {Number}
21316      */
21317     getCount : function(){
21318         return this.selections.length;
21319     },
21320
21321     /**
21322      * Selects the first row in the grid.
21323      */
21324     selectFirstRow : function(){
21325         this.selectRow(0);
21326     },
21327
21328     /**
21329      * Select the last row.
21330      * @param {Boolean} keepExisting (optional) True to keep existing selections
21331      */
21332     selectLastRow : function(keepExisting){
21333         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21334     },
21335
21336     /**
21337      * Selects the row immediately following the last selected row.
21338      * @param {Boolean} keepExisting (optional) True to keep existing selections
21339      */
21340     selectNext : function(keepExisting){
21341         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21342             this.selectRow(this.last+1, keepExisting);
21343             this.grid.getView().focusRow(this.last);
21344         }
21345     },
21346
21347     /**
21348      * Selects the row that precedes the last selected row.
21349      * @param {Boolean} keepExisting (optional) True to keep existing selections
21350      */
21351     selectPrevious : function(keepExisting){
21352         if(this.last){
21353             this.selectRow(this.last-1, keepExisting);
21354             this.grid.getView().focusRow(this.last);
21355         }
21356     },
21357
21358     /**
21359      * Returns the selected records
21360      * @return {Array} Array of selected records
21361      */
21362     getSelections : function(){
21363         return [].concat(this.selections.items);
21364     },
21365
21366     /**
21367      * Returns the first selected record.
21368      * @return {Record}
21369      */
21370     getSelected : function(){
21371         return this.selections.itemAt(0);
21372     },
21373
21374
21375     /**
21376      * Clears all selections.
21377      */
21378     clearSelections : function(fast){
21379         if(this.locked) return;
21380         if(fast !== true){
21381             var ds = this.grid.dataSource;
21382             var s = this.selections;
21383             s.each(function(r){
21384                 this.deselectRow(ds.indexOfId(r.id));
21385             }, this);
21386             s.clear();
21387         }else{
21388             this.selections.clear();
21389         }
21390         this.last = false;
21391     },
21392
21393
21394     /**
21395      * Selects all rows.
21396      */
21397     selectAll : function(){
21398         if(this.locked) return;
21399         this.selections.clear();
21400         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21401             this.selectRow(i, true);
21402         }
21403     },
21404
21405     /**
21406      * Returns True if there is a selection.
21407      * @return {Boolean}
21408      */
21409     hasSelection : function(){
21410         return this.selections.length > 0;
21411     },
21412
21413     /**
21414      * Returns True if the specified row is selected.
21415      * @param {Number/Record} record The record or index of the record to check
21416      * @return {Boolean}
21417      */
21418     isSelected : function(index){
21419         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21420         return (r && this.selections.key(r.id) ? true : false);
21421     },
21422
21423     /**
21424      * Returns True if the specified record id is selected.
21425      * @param {String} id The id of record to check
21426      * @return {Boolean}
21427      */
21428     isIdSelected : function(id){
21429         return (this.selections.key(id) ? true : false);
21430     },
21431
21432     // private
21433     handleMouseDown : function(e, t){
21434         var view = this.grid.getView(), rowIndex;
21435         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21436             return;
21437         };
21438         if(e.shiftKey && this.last !== false){
21439             var last = this.last;
21440             this.selectRange(last, rowIndex, e.ctrlKey);
21441             this.last = last; // reset the last
21442             view.focusRow(rowIndex);
21443         }else{
21444             var isSelected = this.isSelected(rowIndex);
21445             if(e.button !== 0 && isSelected){
21446                 view.focusRow(rowIndex);
21447             }else if(e.ctrlKey && isSelected){
21448                 this.deselectRow(rowIndex);
21449             }else if(!isSelected){
21450                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21451                 view.focusRow(rowIndex);
21452             }
21453         }
21454         this.fireEvent("afterselectionchange", this);
21455     },
21456     // private
21457     handleDragableRowClick :  function(grid, rowIndex, e) 
21458     {
21459         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21460             this.selectRow(rowIndex, false);
21461             grid.view.focusRow(rowIndex);
21462              this.fireEvent("afterselectionchange", this);
21463         }
21464     },
21465     
21466     /**
21467      * Selects multiple rows.
21468      * @param {Array} rows Array of the indexes of the row to select
21469      * @param {Boolean} keepExisting (optional) True to keep existing selections
21470      */
21471     selectRows : function(rows, keepExisting){
21472         if(!keepExisting){
21473             this.clearSelections();
21474         }
21475         for(var i = 0, len = rows.length; i < len; i++){
21476             this.selectRow(rows[i], true);
21477         }
21478     },
21479
21480     /**
21481      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21482      * @param {Number} startRow The index of the first row in the range
21483      * @param {Number} endRow The index of the last row in the range
21484      * @param {Boolean} keepExisting (optional) True to retain existing selections
21485      */
21486     selectRange : function(startRow, endRow, keepExisting){
21487         if(this.locked) return;
21488         if(!keepExisting){
21489             this.clearSelections();
21490         }
21491         if(startRow <= endRow){
21492             for(var i = startRow; i <= endRow; i++){
21493                 this.selectRow(i, true);
21494             }
21495         }else{
21496             for(var i = startRow; i >= endRow; i--){
21497                 this.selectRow(i, true);
21498             }
21499         }
21500     },
21501
21502     /**
21503      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21504      * @param {Number} startRow The index of the first row in the range
21505      * @param {Number} endRow The index of the last row in the range
21506      */
21507     deselectRange : function(startRow, endRow, preventViewNotify){
21508         if(this.locked) return;
21509         for(var i = startRow; i <= endRow; i++){
21510             this.deselectRow(i, preventViewNotify);
21511         }
21512     },
21513
21514     /**
21515      * Selects a row.
21516      * @param {Number} row The index of the row to select
21517      * @param {Boolean} keepExisting (optional) True to keep existing selections
21518      */
21519     selectRow : function(index, keepExisting, preventViewNotify){
21520         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21521         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21522             if(!keepExisting || this.singleSelect){
21523                 this.clearSelections();
21524             }
21525             var r = this.grid.dataSource.getAt(index);
21526             this.selections.add(r);
21527             this.last = this.lastActive = index;
21528             if(!preventViewNotify){
21529                 this.grid.getView().onRowSelect(index);
21530             }
21531             this.fireEvent("rowselect", this, index, r);
21532             this.fireEvent("selectionchange", this);
21533         }
21534     },
21535
21536     /**
21537      * Deselects a row.
21538      * @param {Number} row The index of the row to deselect
21539      */
21540     deselectRow : function(index, preventViewNotify){
21541         if(this.locked) return;
21542         if(this.last == index){
21543             this.last = false;
21544         }
21545         if(this.lastActive == index){
21546             this.lastActive = false;
21547         }
21548         var r = this.grid.dataSource.getAt(index);
21549         this.selections.remove(r);
21550         if(!preventViewNotify){
21551             this.grid.getView().onRowDeselect(index);
21552         }
21553         this.fireEvent("rowdeselect", this, index);
21554         this.fireEvent("selectionchange", this);
21555     },
21556
21557     // private
21558     restoreLast : function(){
21559         if(this._last){
21560             this.last = this._last;
21561         }
21562     },
21563
21564     // private
21565     acceptsNav : function(row, col, cm){
21566         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21567     },
21568
21569     // private
21570     onEditorKey : function(field, e){
21571         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21572         if(k == e.TAB){
21573             e.stopEvent();
21574             ed.completeEdit();
21575             if(e.shiftKey){
21576                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21577             }else{
21578                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21579             }
21580         }else if(k == e.ENTER && !e.ctrlKey){
21581             e.stopEvent();
21582             ed.completeEdit();
21583             if(e.shiftKey){
21584                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21585             }else{
21586                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21587             }
21588         }else if(k == e.ESC){
21589             ed.cancelEdit();
21590         }
21591         if(newCell){
21592             g.startEditing(newCell[0], newCell[1]);
21593         }
21594     }
21595 });/*
21596  * Based on:
21597  * Ext JS Library 1.1.1
21598  * Copyright(c) 2006-2007, Ext JS, LLC.
21599  *
21600  * Originally Released Under LGPL - original licence link has changed is not relivant.
21601  *
21602  * Fork - LGPL
21603  * <script type="text/javascript">
21604  */
21605  
21606 /**
21607  * @class Roo.bootstrap.PagingToolbar
21608  * @extends Roo.Row
21609  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21610  * @constructor
21611  * Create a new PagingToolbar
21612  * @param {Object} config The config object
21613  */
21614 Roo.bootstrap.PagingToolbar = function(config)
21615 {
21616     // old args format still supported... - xtype is prefered..
21617         // created from xtype...
21618     var ds = config.dataSource;
21619     this.toolbarItems = [];
21620     if (config.items) {
21621         this.toolbarItems = config.items;
21622 //        config.items = [];
21623     }
21624     
21625     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21626     this.ds = ds;
21627     this.cursor = 0;
21628     if (ds) { 
21629         this.bind(ds);
21630     }
21631     
21632     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21633     
21634 };
21635
21636 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21637     /**
21638      * @cfg {Roo.data.Store} dataSource
21639      * The underlying data store providing the paged data
21640      */
21641     /**
21642      * @cfg {String/HTMLElement/Element} container
21643      * container The id or element that will contain the toolbar
21644      */
21645     /**
21646      * @cfg {Boolean} displayInfo
21647      * True to display the displayMsg (defaults to false)
21648      */
21649     /**
21650      * @cfg {Number} pageSize
21651      * The number of records to display per page (defaults to 20)
21652      */
21653     pageSize: 20,
21654     /**
21655      * @cfg {String} displayMsg
21656      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21657      */
21658     displayMsg : 'Displaying {0} - {1} of {2}',
21659     /**
21660      * @cfg {String} emptyMsg
21661      * The message to display when no records are found (defaults to "No data to display")
21662      */
21663     emptyMsg : 'No data to display',
21664     /**
21665      * Customizable piece of the default paging text (defaults to "Page")
21666      * @type String
21667      */
21668     beforePageText : "Page",
21669     /**
21670      * Customizable piece of the default paging text (defaults to "of %0")
21671      * @type String
21672      */
21673     afterPageText : "of {0}",
21674     /**
21675      * Customizable piece of the default paging text (defaults to "First Page")
21676      * @type String
21677      */
21678     firstText : "First Page",
21679     /**
21680      * Customizable piece of the default paging text (defaults to "Previous Page")
21681      * @type String
21682      */
21683     prevText : "Previous Page",
21684     /**
21685      * Customizable piece of the default paging text (defaults to "Next Page")
21686      * @type String
21687      */
21688     nextText : "Next Page",
21689     /**
21690      * Customizable piece of the default paging text (defaults to "Last Page")
21691      * @type String
21692      */
21693     lastText : "Last Page",
21694     /**
21695      * Customizable piece of the default paging text (defaults to "Refresh")
21696      * @type String
21697      */
21698     refreshText : "Refresh",
21699
21700     buttons : false,
21701     // private
21702     onRender : function(ct, position) 
21703     {
21704         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21705         this.navgroup.parentId = this.id;
21706         this.navgroup.onRender(this.el, null);
21707         // add the buttons to the navgroup
21708         
21709         if(this.displayInfo){
21710             Roo.log(this.el.select('ul.navbar-nav',true).first());
21711             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21712             this.displayEl = this.el.select('.x-paging-info', true).first();
21713 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21714 //            this.displayEl = navel.el.select('span',true).first();
21715         }
21716         
21717         var _this = this;
21718         
21719         if(this.buttons){
21720             Roo.each(_this.buttons, function(e){
21721                Roo.factory(e).onRender(_this.el, null);
21722             });
21723         }
21724             
21725         Roo.each(_this.toolbarItems, function(e) {
21726             _this.navgroup.addItem(e);
21727         });
21728         
21729         
21730         this.first = this.navgroup.addItem({
21731             tooltip: this.firstText,
21732             cls: "prev",
21733             icon : 'fa fa-backward',
21734             disabled: true,
21735             preventDefault: true,
21736             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21737         });
21738         
21739         this.prev =  this.navgroup.addItem({
21740             tooltip: this.prevText,
21741             cls: "prev",
21742             icon : 'fa fa-step-backward',
21743             disabled: true,
21744             preventDefault: true,
21745             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21746         });
21747     //this.addSeparator();
21748         
21749         
21750         var field = this.navgroup.addItem( {
21751             tagtype : 'span',
21752             cls : 'x-paging-position',
21753             
21754             html : this.beforePageText  +
21755                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21756                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21757          } ); //?? escaped?
21758         
21759         this.field = field.el.select('input', true).first();
21760         this.field.on("keydown", this.onPagingKeydown, this);
21761         this.field.on("focus", function(){this.dom.select();});
21762     
21763     
21764         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21765         //this.field.setHeight(18);
21766         //this.addSeparator();
21767         this.next = this.navgroup.addItem({
21768             tooltip: this.nextText,
21769             cls: "next",
21770             html : ' <i class="fa fa-step-forward">',
21771             disabled: true,
21772             preventDefault: true,
21773             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21774         });
21775         this.last = this.navgroup.addItem({
21776             tooltip: this.lastText,
21777             icon : 'fa fa-forward',
21778             cls: "next",
21779             disabled: true,
21780             preventDefault: true,
21781             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21782         });
21783     //this.addSeparator();
21784         this.loading = this.navgroup.addItem({
21785             tooltip: this.refreshText,
21786             icon: 'fa fa-refresh',
21787             preventDefault: true,
21788             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21789         });
21790
21791     },
21792
21793     // private
21794     updateInfo : function(){
21795         if(this.displayEl){
21796             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21797             var msg = count == 0 ?
21798                 this.emptyMsg :
21799                 String.format(
21800                     this.displayMsg,
21801                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21802                 );
21803             this.displayEl.update(msg);
21804         }
21805     },
21806
21807     // private
21808     onLoad : function(ds, r, o){
21809        this.cursor = o.params ? o.params.start : 0;
21810        var d = this.getPageData(),
21811             ap = d.activePage,
21812             ps = d.pages;
21813         
21814        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21815        this.field.dom.value = ap;
21816        this.first.setDisabled(ap == 1);
21817        this.prev.setDisabled(ap == 1);
21818        this.next.setDisabled(ap == ps);
21819        this.last.setDisabled(ap == ps);
21820        this.loading.enable();
21821        this.updateInfo();
21822     },
21823
21824     // private
21825     getPageData : function(){
21826         var total = this.ds.getTotalCount();
21827         return {
21828             total : total,
21829             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21830             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21831         };
21832     },
21833
21834     // private
21835     onLoadError : function(){
21836         this.loading.enable();
21837     },
21838
21839     // private
21840     onPagingKeydown : function(e){
21841         var k = e.getKey();
21842         var d = this.getPageData();
21843         if(k == e.RETURN){
21844             var v = this.field.dom.value, pageNum;
21845             if(!v || isNaN(pageNum = parseInt(v, 10))){
21846                 this.field.dom.value = d.activePage;
21847                 return;
21848             }
21849             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21850             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21851             e.stopEvent();
21852         }
21853         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))
21854         {
21855           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21856           this.field.dom.value = pageNum;
21857           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21858           e.stopEvent();
21859         }
21860         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21861         {
21862           var v = this.field.dom.value, pageNum; 
21863           var increment = (e.shiftKey) ? 10 : 1;
21864           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21865             increment *= -1;
21866           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21867             this.field.dom.value = d.activePage;
21868             return;
21869           }
21870           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21871           {
21872             this.field.dom.value = parseInt(v, 10) + increment;
21873             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21874             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21875           }
21876           e.stopEvent();
21877         }
21878     },
21879
21880     // private
21881     beforeLoad : function(){
21882         if(this.loading){
21883             this.loading.disable();
21884         }
21885     },
21886
21887     // private
21888     onClick : function(which){
21889         
21890         var ds = this.ds;
21891         if (!ds) {
21892             return;
21893         }
21894         
21895         switch(which){
21896             case "first":
21897                 ds.load({params:{start: 0, limit: this.pageSize}});
21898             break;
21899             case "prev":
21900                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21901             break;
21902             case "next":
21903                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21904             break;
21905             case "last":
21906                 var total = ds.getTotalCount();
21907                 var extra = total % this.pageSize;
21908                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21909                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21910             break;
21911             case "refresh":
21912                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21913             break;
21914         }
21915     },
21916
21917     /**
21918      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21919      * @param {Roo.data.Store} store The data store to unbind
21920      */
21921     unbind : function(ds){
21922         ds.un("beforeload", this.beforeLoad, this);
21923         ds.un("load", this.onLoad, this);
21924         ds.un("loadexception", this.onLoadError, this);
21925         ds.un("remove", this.updateInfo, this);
21926         ds.un("add", this.updateInfo, this);
21927         this.ds = undefined;
21928     },
21929
21930     /**
21931      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21932      * @param {Roo.data.Store} store The data store to bind
21933      */
21934     bind : function(ds){
21935         ds.on("beforeload", this.beforeLoad, this);
21936         ds.on("load", this.onLoad, this);
21937         ds.on("loadexception", this.onLoadError, this);
21938         ds.on("remove", this.updateInfo, this);
21939         ds.on("add", this.updateInfo, this);
21940         this.ds = ds;
21941     }
21942 });/*
21943  * - LGPL
21944  *
21945  * element
21946  * 
21947  */
21948
21949 /**
21950  * @class Roo.bootstrap.MessageBar
21951  * @extends Roo.bootstrap.Component
21952  * Bootstrap MessageBar class
21953  * @cfg {String} html contents of the MessageBar
21954  * @cfg {String} weight (info | success | warning | danger) default info
21955  * @cfg {String} beforeClass insert the bar before the given class
21956  * @cfg {Boolean} closable (true | false) default false
21957  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21958  * 
21959  * @constructor
21960  * Create a new Element
21961  * @param {Object} config The config object
21962  */
21963
21964 Roo.bootstrap.MessageBar = function(config){
21965     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21966 };
21967
21968 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21969     
21970     html: '',
21971     weight: 'info',
21972     closable: false,
21973     fixed: false,
21974     beforeClass: 'bootstrap-sticky-wrap',
21975     
21976     getAutoCreate : function(){
21977         
21978         var cfg = {
21979             tag: 'div',
21980             cls: 'alert alert-dismissable alert-' + this.weight,
21981             cn: [
21982                 {
21983                     tag: 'span',
21984                     cls: 'message',
21985                     html: this.html || ''
21986                 }
21987             ]
21988         }
21989         
21990         if(this.fixed){
21991             cfg.cls += ' alert-messages-fixed';
21992         }
21993         
21994         if(this.closable){
21995             cfg.cn.push({
21996                 tag: 'button',
21997                 cls: 'close',
21998                 html: 'x'
21999             });
22000         }
22001         
22002         return cfg;
22003     },
22004     
22005     onRender : function(ct, position)
22006     {
22007         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22008         
22009         if(!this.el){
22010             var cfg = Roo.apply({},  this.getAutoCreate());
22011             cfg.id = Roo.id();
22012             
22013             if (this.cls) {
22014                 cfg.cls += ' ' + this.cls;
22015             }
22016             if (this.style) {
22017                 cfg.style = this.style;
22018             }
22019             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22020             
22021             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22022         }
22023         
22024         this.el.select('>button.close').on('click', this.hide, this);
22025         
22026     },
22027     
22028     show : function()
22029     {
22030         if (!this.rendered) {
22031             this.render();
22032         }
22033         
22034         this.el.show();
22035         
22036         this.fireEvent('show', this);
22037         
22038     },
22039     
22040     hide : function()
22041     {
22042         if (!this.rendered) {
22043             this.render();
22044         }
22045         
22046         this.el.hide();
22047         
22048         this.fireEvent('hide', this);
22049     },
22050     
22051     update : function()
22052     {
22053 //        var e = this.el.dom.firstChild;
22054 //        
22055 //        if(this.closable){
22056 //            e = e.nextSibling;
22057 //        }
22058 //        
22059 //        e.data = this.html || '';
22060
22061         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22062     }
22063    
22064 });
22065
22066  
22067
22068      /*
22069  * - LGPL
22070  *
22071  * Graph
22072  * 
22073  */
22074
22075
22076 /**
22077  * @class Roo.bootstrap.Graph
22078  * @extends Roo.bootstrap.Component
22079  * Bootstrap Graph class
22080 > Prameters
22081  -sm {number} sm 4
22082  -md {number} md 5
22083  @cfg {String} graphtype  bar | vbar | pie
22084  @cfg {number} g_x coodinator | centre x (pie)
22085  @cfg {number} g_y coodinator | centre y (pie)
22086  @cfg {number} g_r radius (pie)
22087  @cfg {number} g_height height of the chart (respected by all elements in the set)
22088  @cfg {number} g_width width of the chart (respected by all elements in the set)
22089  @cfg {Object} title The title of the chart
22090     
22091  -{Array}  values
22092  -opts (object) options for the chart 
22093      o {
22094      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22095      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22096      o vgutter (number)
22097      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.
22098      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22099      o to
22100      o stretch (boolean)
22101      o }
22102  -opts (object) options for the pie
22103      o{
22104      o cut
22105      o startAngle (number)
22106      o endAngle (number)
22107      } 
22108  *
22109  * @constructor
22110  * Create a new Input
22111  * @param {Object} config The config object
22112  */
22113
22114 Roo.bootstrap.Graph = function(config){
22115     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22116     
22117     this.addEvents({
22118         // img events
22119         /**
22120          * @event click
22121          * The img click event for the img.
22122          * @param {Roo.EventObject} e
22123          */
22124         "click" : true
22125     });
22126 };
22127
22128 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22129     
22130     sm: 4,
22131     md: 5,
22132     graphtype: 'bar',
22133     g_height: 250,
22134     g_width: 400,
22135     g_x: 50,
22136     g_y: 50,
22137     g_r: 30,
22138     opts:{
22139         //g_colors: this.colors,
22140         g_type: 'soft',
22141         g_gutter: '20%'
22142
22143     },
22144     title : false,
22145
22146     getAutoCreate : function(){
22147         
22148         var cfg = {
22149             tag: 'div',
22150             html : null
22151         }
22152         
22153         
22154         return  cfg;
22155     },
22156
22157     onRender : function(ct,position){
22158         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22159         this.raphael = Raphael(this.el.dom);
22160         
22161                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22162                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22163                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22164                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22165                 /*
22166                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22167                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22168                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22169                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22170                 
22171                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22172                 r.barchart(330, 10, 300, 220, data1);
22173                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22174                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22175                 */
22176                 
22177                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22178                 // r.barchart(30, 30, 560, 250,  xdata, {
22179                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22180                 //     axis : "0 0 1 1",
22181                 //     axisxlabels :  xdata
22182                 //     //yvalues : cols,
22183                    
22184                 // });
22185 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22186 //        
22187 //        this.load(null,xdata,{
22188 //                axis : "0 0 1 1",
22189 //                axisxlabels :  xdata
22190 //                });
22191
22192     },
22193
22194     load : function(graphtype,xdata,opts){
22195         this.raphael.clear();
22196         if(!graphtype) {
22197             graphtype = this.graphtype;
22198         }
22199         if(!opts){
22200             opts = this.opts;
22201         }
22202         var r = this.raphael,
22203             fin = function () {
22204                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22205             },
22206             fout = function () {
22207                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22208             },
22209             pfin = function() {
22210                 this.sector.stop();
22211                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22212
22213                 if (this.label) {
22214                     this.label[0].stop();
22215                     this.label[0].attr({ r: 7.5 });
22216                     this.label[1].attr({ "font-weight": 800 });
22217                 }
22218             },
22219             pfout = function() {
22220                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22221
22222                 if (this.label) {
22223                     this.label[0].animate({ r: 5 }, 500, "bounce");
22224                     this.label[1].attr({ "font-weight": 400 });
22225                 }
22226             };
22227
22228         switch(graphtype){
22229             case 'bar':
22230                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22231                 break;
22232             case 'hbar':
22233                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22234                 break;
22235             case 'pie':
22236 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22237 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22238 //            
22239                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22240                 
22241                 break;
22242
22243         }
22244         
22245         if(this.title){
22246             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22247         }
22248         
22249     },
22250     
22251     setTitle: function(o)
22252     {
22253         this.title = o;
22254     },
22255     
22256     initEvents: function() {
22257         
22258         if(!this.href){
22259             this.el.on('click', this.onClick, this);
22260         }
22261     },
22262     
22263     onClick : function(e)
22264     {
22265         Roo.log('img onclick');
22266         this.fireEvent('click', this, e);
22267     }
22268    
22269 });
22270
22271  
22272 /*
22273  * - LGPL
22274  *
22275  * numberBox
22276  * 
22277  */
22278 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22279
22280 /**
22281  * @class Roo.bootstrap.dash.NumberBox
22282  * @extends Roo.bootstrap.Component
22283  * Bootstrap NumberBox class
22284  * @cfg {String} headline Box headline
22285  * @cfg {String} content Box content
22286  * @cfg {String} icon Box icon
22287  * @cfg {String} footer Footer text
22288  * @cfg {String} fhref Footer href
22289  * 
22290  * @constructor
22291  * Create a new NumberBox
22292  * @param {Object} config The config object
22293  */
22294
22295
22296 Roo.bootstrap.dash.NumberBox = function(config){
22297     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22298     
22299 };
22300
22301 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22302     
22303     headline : '',
22304     content : '',
22305     icon : '',
22306     footer : '',
22307     fhref : '',
22308     ficon : '',
22309     
22310     getAutoCreate : function(){
22311         
22312         var cfg = {
22313             tag : 'div',
22314             cls : 'small-box ',
22315             cn : [
22316                 {
22317                     tag : 'div',
22318                     cls : 'inner',
22319                     cn :[
22320                         {
22321                             tag : 'h3',
22322                             cls : 'roo-headline',
22323                             html : this.headline
22324                         },
22325                         {
22326                             tag : 'p',
22327                             cls : 'roo-content',
22328                             html : this.content
22329                         }
22330                     ]
22331                 }
22332             ]
22333         }
22334         
22335         if(this.icon){
22336             cfg.cn.push({
22337                 tag : 'div',
22338                 cls : 'icon',
22339                 cn :[
22340                     {
22341                         tag : 'i',
22342                         cls : 'ion ' + this.icon
22343                     }
22344                 ]
22345             });
22346         }
22347         
22348         if(this.footer){
22349             var footer = {
22350                 tag : 'a',
22351                 cls : 'small-box-footer',
22352                 href : this.fhref || '#',
22353                 html : this.footer
22354             };
22355             
22356             cfg.cn.push(footer);
22357             
22358         }
22359         
22360         return  cfg;
22361     },
22362
22363     onRender : function(ct,position){
22364         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22365
22366
22367        
22368                 
22369     },
22370
22371     setHeadline: function (value)
22372     {
22373         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22374     },
22375     
22376     setFooter: function (value, href)
22377     {
22378         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22379         
22380         if(href){
22381             this.el.select('a.small-box-footer',true).first().attr('href', href);
22382         }
22383         
22384     },
22385
22386     setContent: function (value)
22387     {
22388         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22389     },
22390
22391     initEvents: function() 
22392     {   
22393         
22394     }
22395     
22396 });
22397
22398  
22399 /*
22400  * - LGPL
22401  *
22402  * TabBox
22403  * 
22404  */
22405 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22406
22407 /**
22408  * @class Roo.bootstrap.dash.TabBox
22409  * @extends Roo.bootstrap.Component
22410  * Bootstrap TabBox class
22411  * @cfg {String} title Title of the TabBox
22412  * @cfg {String} icon Icon of the TabBox
22413  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22414  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22415  * 
22416  * @constructor
22417  * Create a new TabBox
22418  * @param {Object} config The config object
22419  */
22420
22421
22422 Roo.bootstrap.dash.TabBox = function(config){
22423     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22424     this.addEvents({
22425         // raw events
22426         /**
22427          * @event addpane
22428          * When a pane is added
22429          * @param {Roo.bootstrap.dash.TabPane} pane
22430          */
22431         "addpane" : true,
22432         /**
22433          * @event activatepane
22434          * When a pane is activated
22435          * @param {Roo.bootstrap.dash.TabPane} pane
22436          */
22437         "activatepane" : true
22438         
22439          
22440     });
22441     
22442     this.panes = [];
22443 };
22444
22445 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22446
22447     title : '',
22448     icon : false,
22449     showtabs : true,
22450     tabScrollable : false,
22451     
22452     getChildContainer : function()
22453     {
22454         return this.el.select('.tab-content', true).first();
22455     },
22456     
22457     getAutoCreate : function(){
22458         
22459         var header = {
22460             tag: 'li',
22461             cls: 'pull-left header',
22462             html: this.title,
22463             cn : []
22464         };
22465         
22466         if(this.icon){
22467             header.cn.push({
22468                 tag: 'i',
22469                 cls: 'fa ' + this.icon
22470             });
22471         }
22472         
22473         var h = {
22474             tag: 'ul',
22475             cls: 'nav nav-tabs pull-right',
22476             cn: [
22477                 header
22478             ]
22479         };
22480         
22481         if(this.tabScrollable){
22482             h = {
22483                 tag: 'div',
22484                 cls: 'tab-header',
22485                 cn: [
22486                     {
22487                         tag: 'ul',
22488                         cls: 'nav nav-tabs pull-right',
22489                         cn: [
22490                             header
22491                         ]
22492                     }
22493                 ]
22494             }
22495         }
22496         
22497         var cfg = {
22498             tag: 'div',
22499             cls: 'nav-tabs-custom',
22500             cn: [
22501                 h,
22502                 {
22503                     tag: 'div',
22504                     cls: 'tab-content no-padding',
22505                     cn: []
22506                 }
22507             ]
22508         }
22509
22510         return  cfg;
22511     },
22512     initEvents : function()
22513     {
22514         //Roo.log('add add pane handler');
22515         this.on('addpane', this.onAddPane, this);
22516     },
22517      /**
22518      * Updates the box title
22519      * @param {String} html to set the title to.
22520      */
22521     setTitle : function(value)
22522     {
22523         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22524     },
22525     onAddPane : function(pane)
22526     {
22527         this.panes.push(pane);
22528         //Roo.log('addpane');
22529         //Roo.log(pane);
22530         // tabs are rendere left to right..
22531         if(!this.showtabs){
22532             return;
22533         }
22534         
22535         var ctr = this.el.select('.nav-tabs', true).first();
22536          
22537          
22538         var existing = ctr.select('.nav-tab',true);
22539         var qty = existing.getCount();;
22540         
22541         
22542         var tab = ctr.createChild({
22543             tag : 'li',
22544             cls : 'nav-tab' + (qty ? '' : ' active'),
22545             cn : [
22546                 {
22547                     tag : 'a',
22548                     href:'#',
22549                     html : pane.title
22550                 }
22551             ]
22552         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22553         pane.tab = tab;
22554         
22555         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22556         if (!qty) {
22557             pane.el.addClass('active');
22558         }
22559         
22560                 
22561     },
22562     onTabClick : function(ev,un,ob,pane)
22563     {
22564         //Roo.log('tab - prev default');
22565         ev.preventDefault();
22566         
22567         
22568         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22569         pane.tab.addClass('active');
22570         //Roo.log(pane.title);
22571         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22572         // technically we should have a deactivate event.. but maybe add later.
22573         // and it should not de-activate the selected tab...
22574         this.fireEvent('activatepane', pane);
22575         pane.el.addClass('active');
22576         pane.fireEvent('activate');
22577         
22578         
22579     },
22580     
22581     getActivePane : function()
22582     {
22583         var r = false;
22584         Roo.each(this.panes, function(p) {
22585             if(p.el.hasClass('active')){
22586                 r = p;
22587                 return false;
22588             }
22589             
22590             return;
22591         });
22592         
22593         return r;
22594     }
22595     
22596     
22597 });
22598
22599  
22600 /*
22601  * - LGPL
22602  *
22603  * Tab pane
22604  * 
22605  */
22606 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22607 /**
22608  * @class Roo.bootstrap.TabPane
22609  * @extends Roo.bootstrap.Component
22610  * Bootstrap TabPane class
22611  * @cfg {Boolean} active (false | true) Default false
22612  * @cfg {String} title title of panel
22613
22614  * 
22615  * @constructor
22616  * Create a new TabPane
22617  * @param {Object} config The config object
22618  */
22619
22620 Roo.bootstrap.dash.TabPane = function(config){
22621     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22622     
22623     this.addEvents({
22624         // raw events
22625         /**
22626          * @event activate
22627          * When a pane is activated
22628          * @param {Roo.bootstrap.dash.TabPane} pane
22629          */
22630         "activate" : true
22631          
22632     });
22633 };
22634
22635 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22636     
22637     active : false,
22638     title : '',
22639     
22640     // the tabBox that this is attached to.
22641     tab : false,
22642      
22643     getAutoCreate : function() 
22644     {
22645         var cfg = {
22646             tag: 'div',
22647             cls: 'tab-pane'
22648         }
22649         
22650         if(this.active){
22651             cfg.cls += ' active';
22652         }
22653         
22654         return cfg;
22655     },
22656     initEvents  : function()
22657     {
22658         //Roo.log('trigger add pane handler');
22659         this.parent().fireEvent('addpane', this)
22660     },
22661     
22662      /**
22663      * Updates the tab title 
22664      * @param {String} html to set the title to.
22665      */
22666     setTitle: function(str)
22667     {
22668         if (!this.tab) {
22669             return;
22670         }
22671         this.title = str;
22672         this.tab.select('a', true).first().dom.innerHTML = str;
22673         
22674     }
22675     
22676     
22677     
22678 });
22679
22680  
22681
22682
22683  /*
22684  * - LGPL
22685  *
22686  * menu
22687  * 
22688  */
22689 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22690
22691 /**
22692  * @class Roo.bootstrap.menu.Menu
22693  * @extends Roo.bootstrap.Component
22694  * Bootstrap Menu class - container for Menu
22695  * @cfg {String} html Text of the menu
22696  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22697  * @cfg {String} icon Font awesome icon
22698  * @cfg {String} pos Menu align to (top | bottom) default bottom
22699  * 
22700  * 
22701  * @constructor
22702  * Create a new Menu
22703  * @param {Object} config The config object
22704  */
22705
22706
22707 Roo.bootstrap.menu.Menu = function(config){
22708     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22709     
22710     this.addEvents({
22711         /**
22712          * @event beforeshow
22713          * Fires before this menu is displayed
22714          * @param {Roo.bootstrap.menu.Menu} this
22715          */
22716         beforeshow : true,
22717         /**
22718          * @event beforehide
22719          * Fires before this menu is hidden
22720          * @param {Roo.bootstrap.menu.Menu} this
22721          */
22722         beforehide : true,
22723         /**
22724          * @event show
22725          * Fires after this menu is displayed
22726          * @param {Roo.bootstrap.menu.Menu} this
22727          */
22728         show : true,
22729         /**
22730          * @event hide
22731          * Fires after this menu is hidden
22732          * @param {Roo.bootstrap.menu.Menu} this
22733          */
22734         hide : true,
22735         /**
22736          * @event click
22737          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22738          * @param {Roo.bootstrap.menu.Menu} this
22739          * @param {Roo.EventObject} e
22740          */
22741         click : true
22742     });
22743     
22744 };
22745
22746 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22747     
22748     submenu : false,
22749     html : '',
22750     weight : 'default',
22751     icon : false,
22752     pos : 'bottom',
22753     
22754     
22755     getChildContainer : function() {
22756         if(this.isSubMenu){
22757             return this.el;
22758         }
22759         
22760         return this.el.select('ul.dropdown-menu', true).first();  
22761     },
22762     
22763     getAutoCreate : function()
22764     {
22765         var text = [
22766             {
22767                 tag : 'span',
22768                 cls : 'roo-menu-text',
22769                 html : this.html
22770             }
22771         ];
22772         
22773         if(this.icon){
22774             text.unshift({
22775                 tag : 'i',
22776                 cls : 'fa ' + this.icon
22777             })
22778         }
22779         
22780         
22781         var cfg = {
22782             tag : 'div',
22783             cls : 'btn-group',
22784             cn : [
22785                 {
22786                     tag : 'button',
22787                     cls : 'dropdown-button btn btn-' + this.weight,
22788                     cn : text
22789                 },
22790                 {
22791                     tag : 'button',
22792                     cls : 'dropdown-toggle btn btn-' + this.weight,
22793                     cn : [
22794                         {
22795                             tag : 'span',
22796                             cls : 'caret'
22797                         }
22798                     ]
22799                 },
22800                 {
22801                     tag : 'ul',
22802                     cls : 'dropdown-menu'
22803                 }
22804             ]
22805             
22806         };
22807         
22808         if(this.pos == 'top'){
22809             cfg.cls += ' dropup';
22810         }
22811         
22812         if(this.isSubMenu){
22813             cfg = {
22814                 tag : 'ul',
22815                 cls : 'dropdown-menu'
22816             }
22817         }
22818         
22819         return cfg;
22820     },
22821     
22822     onRender : function(ct, position)
22823     {
22824         this.isSubMenu = ct.hasClass('dropdown-submenu');
22825         
22826         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22827     },
22828     
22829     initEvents : function() 
22830     {
22831         if(this.isSubMenu){
22832             return;
22833         }
22834         
22835         this.hidden = true;
22836         
22837         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22838         this.triggerEl.on('click', this.onTriggerPress, this);
22839         
22840         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22841         this.buttonEl.on('click', this.onClick, this);
22842         
22843     },
22844     
22845     list : function()
22846     {
22847         if(this.isSubMenu){
22848             return this.el;
22849         }
22850         
22851         return this.el.select('ul.dropdown-menu', true).first();
22852     },
22853     
22854     onClick : function(e)
22855     {
22856         this.fireEvent("click", this, e);
22857     },
22858     
22859     onTriggerPress  : function(e)
22860     {   
22861         if (this.isVisible()) {
22862             this.hide();
22863         } else {
22864             this.show();
22865         }
22866     },
22867     
22868     isVisible : function(){
22869         return !this.hidden;
22870     },
22871     
22872     show : function()
22873     {
22874         this.fireEvent("beforeshow", this);
22875         
22876         this.hidden = false;
22877         this.el.addClass('open');
22878         
22879         Roo.get(document).on("mouseup", this.onMouseUp, this);
22880         
22881         this.fireEvent("show", this);
22882         
22883         
22884     },
22885     
22886     hide : function()
22887     {
22888         this.fireEvent("beforehide", this);
22889         
22890         this.hidden = true;
22891         this.el.removeClass('open');
22892         
22893         Roo.get(document).un("mouseup", this.onMouseUp);
22894         
22895         this.fireEvent("hide", this);
22896     },
22897     
22898     onMouseUp : function()
22899     {
22900         this.hide();
22901     }
22902     
22903 });
22904
22905  
22906  /*
22907  * - LGPL
22908  *
22909  * menu item
22910  * 
22911  */
22912 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22913
22914 /**
22915  * @class Roo.bootstrap.menu.Item
22916  * @extends Roo.bootstrap.Component
22917  * Bootstrap MenuItem class
22918  * @cfg {Boolean} submenu (true | false) default false
22919  * @cfg {String} html text of the item
22920  * @cfg {String} href the link
22921  * @cfg {Boolean} disable (true | false) default false
22922  * @cfg {Boolean} preventDefault (true | false) default true
22923  * @cfg {String} icon Font awesome icon
22924  * @cfg {String} pos Submenu align to (left | right) default right 
22925  * 
22926  * 
22927  * @constructor
22928  * Create a new Item
22929  * @param {Object} config The config object
22930  */
22931
22932
22933 Roo.bootstrap.menu.Item = function(config){
22934     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22935     this.addEvents({
22936         /**
22937          * @event mouseover
22938          * Fires when the mouse is hovering over this menu
22939          * @param {Roo.bootstrap.menu.Item} this
22940          * @param {Roo.EventObject} e
22941          */
22942         mouseover : true,
22943         /**
22944          * @event mouseout
22945          * Fires when the mouse exits this menu
22946          * @param {Roo.bootstrap.menu.Item} this
22947          * @param {Roo.EventObject} e
22948          */
22949         mouseout : true,
22950         // raw events
22951         /**
22952          * @event click
22953          * The raw click event for the entire grid.
22954          * @param {Roo.EventObject} e
22955          */
22956         click : true
22957     });
22958 };
22959
22960 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22961     
22962     submenu : false,
22963     href : '',
22964     html : '',
22965     preventDefault: true,
22966     disable : false,
22967     icon : false,
22968     pos : 'right',
22969     
22970     getAutoCreate : function()
22971     {
22972         var text = [
22973             {
22974                 tag : 'span',
22975                 cls : 'roo-menu-item-text',
22976                 html : this.html
22977             }
22978         ];
22979         
22980         if(this.icon){
22981             text.unshift({
22982                 tag : 'i',
22983                 cls : 'fa ' + this.icon
22984             })
22985         }
22986         
22987         var cfg = {
22988             tag : 'li',
22989             cn : [
22990                 {
22991                     tag : 'a',
22992                     href : this.href || '#',
22993                     cn : text
22994                 }
22995             ]
22996         };
22997         
22998         if(this.disable){
22999             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23000         }
23001         
23002         if(this.submenu){
23003             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23004             
23005             if(this.pos == 'left'){
23006                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23007             }
23008         }
23009         
23010         return cfg;
23011     },
23012     
23013     initEvents : function() 
23014     {
23015         this.el.on('mouseover', this.onMouseOver, this);
23016         this.el.on('mouseout', this.onMouseOut, this);
23017         
23018         this.el.select('a', true).first().on('click', this.onClick, this);
23019         
23020     },
23021     
23022     onClick : function(e)
23023     {
23024         if(this.preventDefault){
23025             e.preventDefault();
23026         }
23027         
23028         this.fireEvent("click", this, e);
23029     },
23030     
23031     onMouseOver : function(e)
23032     {
23033         if(this.submenu && this.pos == 'left'){
23034             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23035         }
23036         
23037         this.fireEvent("mouseover", this, e);
23038     },
23039     
23040     onMouseOut : function(e)
23041     {
23042         this.fireEvent("mouseout", this, e);
23043     }
23044 });
23045
23046  
23047
23048  /*
23049  * - LGPL
23050  *
23051  * menu separator
23052  * 
23053  */
23054 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23055
23056 /**
23057  * @class Roo.bootstrap.menu.Separator
23058  * @extends Roo.bootstrap.Component
23059  * Bootstrap Separator class
23060  * 
23061  * @constructor
23062  * Create a new Separator
23063  * @param {Object} config The config object
23064  */
23065
23066
23067 Roo.bootstrap.menu.Separator = function(config){
23068     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23069 };
23070
23071 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23072     
23073     getAutoCreate : function(){
23074         var cfg = {
23075             tag : 'li',
23076             cls: 'divider'
23077         };
23078         
23079         return cfg;
23080     }
23081    
23082 });
23083
23084  
23085
23086  /*
23087  * - LGPL
23088  *
23089  * Tooltip
23090  * 
23091  */
23092
23093 /**
23094  * @class Roo.bootstrap.Tooltip
23095  * Bootstrap Tooltip class
23096  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23097  * to determine which dom element triggers the tooltip.
23098  * 
23099  * It needs to add support for additional attributes like tooltip-position
23100  * 
23101  * @constructor
23102  * Create a new Toolti
23103  * @param {Object} config The config object
23104  */
23105
23106 Roo.bootstrap.Tooltip = function(config){
23107     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23108 };
23109
23110 Roo.apply(Roo.bootstrap.Tooltip, {
23111     /**
23112      * @function init initialize tooltip monitoring.
23113      * @static
23114      */
23115     currentEl : false,
23116     currentTip : false,
23117     currentRegion : false,
23118     
23119     //  init : delay?
23120     
23121     init : function()
23122     {
23123         Roo.get(document).on('mouseover', this.enter ,this);
23124         Roo.get(document).on('mouseout', this.leave, this);
23125          
23126         
23127         this.currentTip = new Roo.bootstrap.Tooltip();
23128     },
23129     
23130     enter : function(ev)
23131     {
23132         var dom = ev.getTarget();
23133         
23134         //Roo.log(['enter',dom]);
23135         var el = Roo.fly(dom);
23136         if (this.currentEl) {
23137             //Roo.log(dom);
23138             //Roo.log(this.currentEl);
23139             //Roo.log(this.currentEl.contains(dom));
23140             if (this.currentEl == el) {
23141                 return;
23142             }
23143             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23144                 return;
23145             }
23146
23147         }
23148         
23149         
23150         
23151         if (this.currentTip.el) {
23152             this.currentTip.el.hide(); // force hiding...
23153         }    
23154         //Roo.log(ev);
23155         var bindEl = el;
23156         
23157         // you can not look for children, as if el is the body.. then everythign is the child..
23158         if (!el.attr('tooltip')) { //
23159             if (!el.select("[tooltip]").elements.length) {
23160                 return;
23161             }
23162             // is the mouse over this child...?
23163             bindEl = el.select("[tooltip]").first();
23164             var xy = ev.getXY();
23165             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23166                 //Roo.log("not in region.");
23167                 return;
23168             }
23169             //Roo.log("child element over..");
23170             
23171         }
23172         this.currentEl = bindEl;
23173         this.currentTip.bind(bindEl);
23174         this.currentRegion = Roo.lib.Region.getRegion(dom);
23175         this.currentTip.enter();
23176         
23177     },
23178     leave : function(ev)
23179     {
23180         var dom = ev.getTarget();
23181         //Roo.log(['leave',dom]);
23182         if (!this.currentEl) {
23183             return;
23184         }
23185         
23186         
23187         if (dom != this.currentEl.dom) {
23188             return;
23189         }
23190         var xy = ev.getXY();
23191         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23192             return;
23193         }
23194         // only activate leave if mouse cursor is outside... bounding box..
23195         
23196         
23197         
23198         
23199         if (this.currentTip) {
23200             this.currentTip.leave();
23201         }
23202         //Roo.log('clear currentEl');
23203         this.currentEl = false;
23204         
23205         
23206     },
23207     alignment : {
23208         'left' : ['r-l', [-2,0], 'right'],
23209         'right' : ['l-r', [2,0], 'left'],
23210         'bottom' : ['t-b', [0,2], 'top'],
23211         'top' : [ 'b-t', [0,-2], 'bottom']
23212     }
23213     
23214 });
23215
23216
23217 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23218     
23219     
23220     bindEl : false,
23221     
23222     delay : null, // can be { show : 300 , hide: 500}
23223     
23224     timeout : null,
23225     
23226     hoverState : null, //???
23227     
23228     placement : 'bottom', 
23229     
23230     getAutoCreate : function(){
23231     
23232         var cfg = {
23233            cls : 'tooltip',
23234            role : 'tooltip',
23235            cn : [
23236                 {
23237                     cls : 'tooltip-arrow'
23238                 },
23239                 {
23240                     cls : 'tooltip-inner'
23241                 }
23242            ]
23243         };
23244         
23245         return cfg;
23246     },
23247     bind : function(el)
23248     {
23249         this.bindEl = el;
23250     },
23251       
23252     
23253     enter : function () {
23254        
23255         if (this.timeout != null) {
23256             clearTimeout(this.timeout);
23257         }
23258         
23259         this.hoverState = 'in';
23260          //Roo.log("enter - show");
23261         if (!this.delay || !this.delay.show) {
23262             this.show();
23263             return;
23264         }
23265         var _t = this;
23266         this.timeout = setTimeout(function () {
23267             if (_t.hoverState == 'in') {
23268                 _t.show();
23269             }
23270         }, this.delay.show);
23271     },
23272     leave : function()
23273     {
23274         clearTimeout(this.timeout);
23275     
23276         this.hoverState = 'out';
23277          if (!this.delay || !this.delay.hide) {
23278             this.hide();
23279             return;
23280         }
23281        
23282         var _t = this;
23283         this.timeout = setTimeout(function () {
23284             //Roo.log("leave - timeout");
23285             
23286             if (_t.hoverState == 'out') {
23287                 _t.hide();
23288                 Roo.bootstrap.Tooltip.currentEl = false;
23289             }
23290         }, delay);
23291     },
23292     
23293     show : function ()
23294     {
23295         if (!this.el) {
23296             this.render(document.body);
23297         }
23298         // set content.
23299         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23300         
23301         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23302         
23303         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23304         
23305         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23306         
23307         var placement = typeof this.placement == 'function' ?
23308             this.placement.call(this, this.el, on_el) :
23309             this.placement;
23310             
23311         var autoToken = /\s?auto?\s?/i;
23312         var autoPlace = autoToken.test(placement);
23313         if (autoPlace) {
23314             placement = placement.replace(autoToken, '') || 'top';
23315         }
23316         
23317         //this.el.detach()
23318         //this.el.setXY([0,0]);
23319         this.el.show();
23320         //this.el.dom.style.display='block';
23321         this.el.addClass(placement);
23322         
23323         //this.el.appendTo(on_el);
23324         
23325         var p = this.getPosition();
23326         var box = this.el.getBox();
23327         
23328         if (autoPlace) {
23329             // fixme..
23330         }
23331         var align = Roo.bootstrap.Tooltip.alignment[placement];
23332         this.el.alignTo(this.bindEl, align[0],align[1]);
23333         //var arrow = this.el.select('.arrow',true).first();
23334         //arrow.set(align[2], 
23335         
23336         this.el.addClass('in fade');
23337         this.hoverState = null;
23338         
23339         if (this.el.hasClass('fade')) {
23340             // fade it?
23341         }
23342         
23343     },
23344     hide : function()
23345     {
23346          
23347         if (!this.el) {
23348             return;
23349         }
23350         //this.el.setXY([0,0]);
23351         this.el.removeClass('in');
23352         //this.el.hide();
23353         
23354     }
23355     
23356 });
23357  
23358
23359  /*
23360  * - LGPL
23361  *
23362  * Location Picker
23363  * 
23364  */
23365
23366 /**
23367  * @class Roo.bootstrap.LocationPicker
23368  * @extends Roo.bootstrap.Component
23369  * Bootstrap LocationPicker class
23370  * @cfg {Number} latitude Position when init default 0
23371  * @cfg {Number} longitude Position when init default 0
23372  * @cfg {Number} zoom default 15
23373  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23374  * @cfg {Boolean} mapTypeControl default false
23375  * @cfg {Boolean} disableDoubleClickZoom default false
23376  * @cfg {Boolean} scrollwheel default true
23377  * @cfg {Boolean} streetViewControl default false
23378  * @cfg {Number} radius default 0
23379  * @cfg {String} locationName
23380  * @cfg {Boolean} draggable default true
23381  * @cfg {Boolean} enableAutocomplete default false
23382  * @cfg {Boolean} enableReverseGeocode default true
23383  * @cfg {String} markerTitle
23384  * 
23385  * @constructor
23386  * Create a new LocationPicker
23387  * @param {Object} config The config object
23388  */
23389
23390
23391 Roo.bootstrap.LocationPicker = function(config){
23392     
23393     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23394     
23395     this.addEvents({
23396         /**
23397          * @event initial
23398          * Fires when the picker initialized.
23399          * @param {Roo.bootstrap.LocationPicker} this
23400          * @param {Google Location} location
23401          */
23402         initial : true,
23403         /**
23404          * @event positionchanged
23405          * Fires when the picker position changed.
23406          * @param {Roo.bootstrap.LocationPicker} this
23407          * @param {Google Location} location
23408          */
23409         positionchanged : true,
23410         /**
23411          * @event resize
23412          * Fires when the map resize.
23413          * @param {Roo.bootstrap.LocationPicker} this
23414          */
23415         resize : true,
23416         /**
23417          * @event show
23418          * Fires when the map show.
23419          * @param {Roo.bootstrap.LocationPicker} this
23420          */
23421         show : true,
23422         /**
23423          * @event hide
23424          * Fires when the map hide.
23425          * @param {Roo.bootstrap.LocationPicker} this
23426          */
23427         hide : true,
23428         /**
23429          * @event mapClick
23430          * Fires when click the map.
23431          * @param {Roo.bootstrap.LocationPicker} this
23432          * @param {Map event} e
23433          */
23434         mapClick : true,
23435         /**
23436          * @event mapRightClick
23437          * Fires when right click the map.
23438          * @param {Roo.bootstrap.LocationPicker} this
23439          * @param {Map event} e
23440          */
23441         mapRightClick : true,
23442         /**
23443          * @event markerClick
23444          * Fires when click the marker.
23445          * @param {Roo.bootstrap.LocationPicker} this
23446          * @param {Map event} e
23447          */
23448         markerClick : true,
23449         /**
23450          * @event markerRightClick
23451          * Fires when right click the marker.
23452          * @param {Roo.bootstrap.LocationPicker} this
23453          * @param {Map event} e
23454          */
23455         markerRightClick : true,
23456         /**
23457          * @event OverlayViewDraw
23458          * Fires when OverlayView Draw
23459          * @param {Roo.bootstrap.LocationPicker} this
23460          */
23461         OverlayViewDraw : true,
23462         /**
23463          * @event OverlayViewOnAdd
23464          * Fires when OverlayView Draw
23465          * @param {Roo.bootstrap.LocationPicker} this
23466          */
23467         OverlayViewOnAdd : true,
23468         /**
23469          * @event OverlayViewOnRemove
23470          * Fires when OverlayView Draw
23471          * @param {Roo.bootstrap.LocationPicker} this
23472          */
23473         OverlayViewOnRemove : true,
23474         /**
23475          * @event OverlayViewShow
23476          * Fires when OverlayView Draw
23477          * @param {Roo.bootstrap.LocationPicker} this
23478          * @param {Pixel} cpx
23479          */
23480         OverlayViewShow : true,
23481         /**
23482          * @event OverlayViewHide
23483          * Fires when OverlayView Draw
23484          * @param {Roo.bootstrap.LocationPicker} this
23485          */
23486         OverlayViewHide : true
23487     });
23488         
23489 };
23490
23491 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23492     
23493     gMapContext: false,
23494     
23495     latitude: 0,
23496     longitude: 0,
23497     zoom: 15,
23498     mapTypeId: false,
23499     mapTypeControl: false,
23500     disableDoubleClickZoom: false,
23501     scrollwheel: true,
23502     streetViewControl: false,
23503     radius: 0,
23504     locationName: '',
23505     draggable: true,
23506     enableAutocomplete: false,
23507     enableReverseGeocode: true,
23508     markerTitle: '',
23509     
23510     getAutoCreate: function()
23511     {
23512
23513         var cfg = {
23514             tag: 'div',
23515             cls: 'roo-location-picker'
23516         };
23517         
23518         return cfg
23519     },
23520     
23521     initEvents: function(ct, position)
23522     {       
23523         if(!this.el.getWidth() || this.isApplied()){
23524             return;
23525         }
23526         
23527         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23528         
23529         this.initial();
23530     },
23531     
23532     initial: function()
23533     {
23534         if(!this.mapTypeId){
23535             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23536         }
23537         
23538         this.gMapContext = this.GMapContext();
23539         
23540         this.initOverlayView();
23541         
23542         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23543         
23544         var _this = this;
23545                 
23546         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23547             _this.setPosition(_this.gMapContext.marker.position);
23548         });
23549         
23550         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23551             _this.fireEvent('mapClick', this, event);
23552             
23553         });
23554
23555         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23556             _this.fireEvent('mapRightClick', this, event);
23557             
23558         });
23559         
23560         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23561             _this.fireEvent('markerClick', this, event);
23562             
23563         });
23564
23565         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23566             _this.fireEvent('markerRightClick', this, event);
23567             
23568         });
23569         
23570         this.setPosition(this.gMapContext.location);
23571         
23572         this.fireEvent('initial', this, this.gMapContext.location);
23573     },
23574     
23575     initOverlayView: function()
23576     {
23577         var _this = this;
23578         
23579         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23580             
23581             draw: function()
23582             {
23583                 _this.fireEvent('OverlayViewDraw', _this);
23584             },
23585             
23586             onAdd: function()
23587             {
23588                 _this.fireEvent('OverlayViewOnAdd', _this);
23589             },
23590             
23591             onRemove: function()
23592             {
23593                 _this.fireEvent('OverlayViewOnRemove', _this);
23594             },
23595             
23596             show: function(cpx)
23597             {
23598                 _this.fireEvent('OverlayViewShow', _this, cpx);
23599             },
23600             
23601             hide: function()
23602             {
23603                 _this.fireEvent('OverlayViewHide', _this);
23604             }
23605             
23606         });
23607     },
23608     
23609     fromLatLngToContainerPixel: function(event)
23610     {
23611         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23612     },
23613     
23614     isApplied: function() 
23615     {
23616         return this.getGmapContext() == false ? false : true;
23617     },
23618     
23619     getGmapContext: function() 
23620     {
23621         return this.gMapContext
23622     },
23623     
23624     GMapContext: function() 
23625     {
23626         var position = new google.maps.LatLng(this.latitude, this.longitude);
23627         
23628         var _map = new google.maps.Map(this.el.dom, {
23629             center: position,
23630             zoom: this.zoom,
23631             mapTypeId: this.mapTypeId,
23632             mapTypeControl: this.mapTypeControl,
23633             disableDoubleClickZoom: this.disableDoubleClickZoom,
23634             scrollwheel: this.scrollwheel,
23635             streetViewControl: this.streetViewControl,
23636             locationName: this.locationName,
23637             draggable: this.draggable,
23638             enableAutocomplete: this.enableAutocomplete,
23639             enableReverseGeocode: this.enableReverseGeocode
23640         });
23641         
23642         var _marker = new google.maps.Marker({
23643             position: position,
23644             map: _map,
23645             title: this.markerTitle,
23646             draggable: this.draggable
23647         });
23648         
23649         return {
23650             map: _map,
23651             marker: _marker,
23652             circle: null,
23653             location: position,
23654             radius: this.radius,
23655             locationName: this.locationName,
23656             addressComponents: {
23657                 formatted_address: null,
23658                 addressLine1: null,
23659                 addressLine2: null,
23660                 streetName: null,
23661                 streetNumber: null,
23662                 city: null,
23663                 district: null,
23664                 state: null,
23665                 stateOrProvince: null
23666             },
23667             settings: this,
23668             domContainer: this.el.dom,
23669             geodecoder: new google.maps.Geocoder()
23670         };
23671     },
23672     
23673     drawCircle: function(center, radius, options) 
23674     {
23675         if (this.gMapContext.circle != null) {
23676             this.gMapContext.circle.setMap(null);
23677         }
23678         if (radius > 0) {
23679             radius *= 1;
23680             options = Roo.apply({}, options, {
23681                 strokeColor: "#0000FF",
23682                 strokeOpacity: .35,
23683                 strokeWeight: 2,
23684                 fillColor: "#0000FF",
23685                 fillOpacity: .2
23686             });
23687             
23688             options.map = this.gMapContext.map;
23689             options.radius = radius;
23690             options.center = center;
23691             this.gMapContext.circle = new google.maps.Circle(options);
23692             return this.gMapContext.circle;
23693         }
23694         
23695         return null;
23696     },
23697     
23698     setPosition: function(location) 
23699     {
23700         this.gMapContext.location = location;
23701         this.gMapContext.marker.setPosition(location);
23702         this.gMapContext.map.panTo(location);
23703         this.drawCircle(location, this.gMapContext.radius, {});
23704         
23705         var _this = this;
23706         
23707         if (this.gMapContext.settings.enableReverseGeocode) {
23708             this.gMapContext.geodecoder.geocode({
23709                 latLng: this.gMapContext.location
23710             }, function(results, status) {
23711                 
23712                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23713                     _this.gMapContext.locationName = results[0].formatted_address;
23714                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23715                     
23716                     _this.fireEvent('positionchanged', this, location);
23717                 }
23718             });
23719             
23720             return;
23721         }
23722         
23723         this.fireEvent('positionchanged', this, location);
23724     },
23725     
23726     resize: function()
23727     {
23728         google.maps.event.trigger(this.gMapContext.map, "resize");
23729         
23730         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23731         
23732         this.fireEvent('resize', this);
23733     },
23734     
23735     setPositionByLatLng: function(latitude, longitude)
23736     {
23737         this.setPosition(new google.maps.LatLng(latitude, longitude));
23738     },
23739     
23740     getCurrentPosition: function() 
23741     {
23742         return {
23743             latitude: this.gMapContext.location.lat(),
23744             longitude: this.gMapContext.location.lng()
23745         };
23746     },
23747     
23748     getAddressName: function() 
23749     {
23750         return this.gMapContext.locationName;
23751     },
23752     
23753     getAddressComponents: function() 
23754     {
23755         return this.gMapContext.addressComponents;
23756     },
23757     
23758     address_component_from_google_geocode: function(address_components) 
23759     {
23760         var result = {};
23761         
23762         for (var i = 0; i < address_components.length; i++) {
23763             var component = address_components[i];
23764             if (component.types.indexOf("postal_code") >= 0) {
23765                 result.postalCode = component.short_name;
23766             } else if (component.types.indexOf("street_number") >= 0) {
23767                 result.streetNumber = component.short_name;
23768             } else if (component.types.indexOf("route") >= 0) {
23769                 result.streetName = component.short_name;
23770             } else if (component.types.indexOf("neighborhood") >= 0) {
23771                 result.city = component.short_name;
23772             } else if (component.types.indexOf("locality") >= 0) {
23773                 result.city = component.short_name;
23774             } else if (component.types.indexOf("sublocality") >= 0) {
23775                 result.district = component.short_name;
23776             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23777                 result.stateOrProvince = component.short_name;
23778             } else if (component.types.indexOf("country") >= 0) {
23779                 result.country = component.short_name;
23780             }
23781         }
23782         
23783         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23784         result.addressLine2 = "";
23785         return result;
23786     },
23787     
23788     setZoomLevel: function(zoom)
23789     {
23790         this.gMapContext.map.setZoom(zoom);
23791     },
23792     
23793     show: function()
23794     {
23795         if(!this.el){
23796             return;
23797         }
23798         
23799         this.el.show();
23800         
23801         this.resize();
23802         
23803         this.fireEvent('show', this);
23804     },
23805     
23806     hide: function()
23807     {
23808         if(!this.el){
23809             return;
23810         }
23811         
23812         this.el.hide();
23813         
23814         this.fireEvent('hide', this);
23815     }
23816     
23817 });
23818
23819 Roo.apply(Roo.bootstrap.LocationPicker, {
23820     
23821     OverlayView : function(map, options)
23822     {
23823         options = options || {};
23824         
23825         this.setMap(map);
23826     }
23827     
23828     
23829 });/*
23830  * - LGPL
23831  *
23832  * Alert
23833  * 
23834  */
23835
23836 /**
23837  * @class Roo.bootstrap.Alert
23838  * @extends Roo.bootstrap.Component
23839  * Bootstrap Alert class
23840  * @cfg {String} title The title of alert
23841  * @cfg {String} html The content of alert
23842  * @cfg {String} weight (  success | info | warning | danger )
23843  * @cfg {String} faicon font-awesomeicon
23844  * 
23845  * @constructor
23846  * Create a new alert
23847  * @param {Object} config The config object
23848  */
23849
23850
23851 Roo.bootstrap.Alert = function(config){
23852     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23853     
23854 };
23855
23856 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23857     
23858     title: '',
23859     html: '',
23860     weight: false,
23861     faicon: false,
23862     
23863     getAutoCreate : function()
23864     {
23865         
23866         var cfg = {
23867             tag : 'div',
23868             cls : 'alert',
23869             cn : [
23870                 {
23871                     tag : 'i',
23872                     cls : 'roo-alert-icon'
23873                     
23874                 },
23875                 {
23876                     tag : 'b',
23877                     cls : 'roo-alert-title',
23878                     html : this.title
23879                 },
23880                 {
23881                     tag : 'span',
23882                     cls : 'roo-alert-text',
23883                     html : this.html
23884                 }
23885             ]
23886         };
23887         
23888         if(this.faicon){
23889             cfg.cn[0].cls += ' fa ' + this.faicon;
23890         }
23891         
23892         if(this.weight){
23893             cfg.cls += ' alert-' + this.weight;
23894         }
23895         
23896         return cfg;
23897     },
23898     
23899     initEvents: function() 
23900     {
23901         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23902     },
23903     
23904     setTitle : function(str)
23905     {
23906         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23907     },
23908     
23909     setText : function(str)
23910     {
23911         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23912     },
23913     
23914     setWeight : function(weight)
23915     {
23916         if(this.weight){
23917             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23918         }
23919         
23920         this.weight = weight;
23921         
23922         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23923     },
23924     
23925     setIcon : function(icon)
23926     {
23927         if(this.faicon){
23928             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23929         }
23930         
23931         this.faicon = icon
23932         
23933         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23934     },
23935     
23936     hide: function() 
23937     {
23938         this.el.hide();   
23939     },
23940     
23941     show: function() 
23942     {  
23943         this.el.show();   
23944     }
23945     
23946 });
23947
23948  
23949 /*
23950 * Licence: LGPL
23951 */
23952
23953 /**
23954  * @class Roo.bootstrap.UploadCropbox
23955  * @extends Roo.bootstrap.Component
23956  * Bootstrap UploadCropbox class
23957  * @cfg {String} emptyText show when image has been loaded
23958  * @cfg {String} rotateNotify show when image too small to rotate
23959  * @cfg {Number} errorTimeout default 3000
23960  * @cfg {Number} minWidth default 300
23961  * @cfg {Number} minHeight default 300
23962  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23963  * 
23964  * @constructor
23965  * Create a new UploadCropbox
23966  * @param {Object} config The config object
23967  */
23968
23969 Roo.bootstrap.UploadCropbox = function(config){
23970     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23971     
23972     this.addEvents({
23973         /**
23974          * @event beforeselectfile
23975          * Fire before select file
23976          * @param {Roo.bootstrap.UploadCropbox} this
23977          */
23978         "beforeselectfile" : true,
23979         /**
23980          * @event initial
23981          * Fire after initEvent
23982          * @param {Roo.bootstrap.UploadCropbox} this
23983          */
23984         "initial" : true,
23985         /**
23986          * @event crop
23987          * Fire after initEvent
23988          * @param {Roo.bootstrap.UploadCropbox} this
23989          * @param {String} data
23990          */
23991         "crop" : true,
23992         /**
23993          * @event prepare
23994          * Fire when preparing the file data
23995          * @param {Roo.bootstrap.UploadCropbox} this
23996          * @param {Object} file
23997          */
23998         "prepare" : true,
23999         /**
24000          * @event exception
24001          * Fire when get exception
24002          * @param {Roo.bootstrap.UploadCropbox} this
24003          * @param {Object} options
24004          */
24005         "exception" : true,
24006         /**
24007          * @event beforeloadcanvas
24008          * Fire before load the canvas
24009          * @param {Roo.bootstrap.UploadCropbox} this
24010          * @param {String} src
24011          */
24012         "beforeloadcanvas" : true,
24013         /**
24014          * @event trash
24015          * Fire when trash image
24016          * @param {Roo.bootstrap.UploadCropbox} this
24017          */
24018         "trash" : true,
24019         /**
24020          * @event download
24021          * Fire when download the image
24022          * @param {Roo.bootstrap.UploadCropbox} this
24023          */
24024         "download" : true,
24025         /**
24026          * @event footerbuttonclick
24027          * Fire when footerbuttonclick
24028          * @param {Roo.bootstrap.UploadCropbox} this
24029          * @param {String} type
24030          */
24031         "footerbuttonclick" : true,
24032         /**
24033          * @event resize
24034          * Fire when resize
24035          * @param {Roo.bootstrap.UploadCropbox} this
24036          */
24037         "resize" : true
24038         
24039     });
24040     
24041     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24042 };
24043
24044 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24045     
24046     emptyText : 'Click to upload image',
24047     rotateNotify : 'Image is too small to rotate',
24048     errorTimeout : 3000,
24049     scale : 0,
24050     baseScale : 1,
24051     rotate : 0,
24052     dragable : false,
24053     pinching : false,
24054     mouseX : 0,
24055     mouseY : 0,
24056     cropData : false,
24057     minWidth : 300,
24058     minHeight : 300,
24059     file : false,
24060     exif : {},
24061     baseRotate : 1,
24062     cropType : 'image/jpeg',
24063     buttons : false,
24064     canvasLoaded : false,
24065     
24066     getAutoCreate : function()
24067     {
24068         var cfg = {
24069             tag : 'div',
24070             cls : 'roo-upload-cropbox',
24071             cn : [
24072                 {
24073                     tag : 'div',
24074                     cls : 'roo-upload-cropbox-body',
24075                     style : 'cursor:pointer',
24076                     cn : [
24077                         {
24078                             tag : 'div',
24079                             cls : 'roo-upload-cropbox-preview'
24080                         },
24081                         {
24082                             tag : 'div',
24083                             cls : 'roo-upload-cropbox-thumb'
24084                         },
24085                         {
24086                             tag : 'div',
24087                             cls : 'roo-upload-cropbox-empty-notify',
24088                             html : this.emptyText
24089                         },
24090                         {
24091                             tag : 'div',
24092                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24093                             html : this.rotateNotify
24094                         }
24095                     ]
24096                 },
24097                 {
24098                     tag : 'div',
24099                     cls : 'roo-upload-cropbox-footer',
24100                     cn : {
24101                         tag : 'div',
24102                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24103                         cn : []
24104                     }
24105                 }
24106             ]
24107         };
24108         
24109         return cfg;
24110     },
24111     
24112     onRender : function(ct, position)
24113     {
24114         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24115         
24116         if (this.buttons.length) {
24117             
24118             Roo.each(this.buttons, function(bb) {
24119                 
24120                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24121                 
24122                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24123                 
24124             }, this);
24125         }
24126     },
24127     
24128     initEvents : function()
24129     {
24130         this.urlAPI = (window.createObjectURL && window) || 
24131                                 (window.URL && URL.revokeObjectURL && URL) || 
24132                                 (window.webkitURL && webkitURL);
24133                         
24134         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24135         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24136         
24137         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24138         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24139         
24140         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24141         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24142         this.thumbEl.hide();
24143         
24144         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24145         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24146         
24147         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24148         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24149         this.errorEl.hide();
24150         
24151         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24152         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24153         this.footerEl.hide();
24154         
24155         this.setThumbBoxSize();
24156         
24157         this.bind();
24158         
24159         this.resize();
24160         
24161         this.fireEvent('initial', this);
24162     },
24163
24164     bind : function()
24165     {
24166         var _this = this;
24167         
24168         window.addEventListener("resize", function() { _this.resize(); } );
24169         
24170         this.bodyEl.on('click', this.beforeSelectFile, this);
24171         
24172         if(Roo.isTouch){
24173             this.bodyEl.on('touchstart', this.onTouchStart, this);
24174             this.bodyEl.on('touchmove', this.onTouchMove, this);
24175             this.bodyEl.on('touchend', this.onTouchEnd, this);
24176         }
24177         
24178         if(!Roo.isTouch){
24179             this.bodyEl.on('mousedown', this.onMouseDown, this);
24180             this.bodyEl.on('mousemove', this.onMouseMove, this);
24181             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24182             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24183             Roo.get(document).on('mouseup', this.onMouseUp, this);
24184         }
24185     },
24186     
24187     reset : function()
24188     {    
24189         this.scale = 0;
24190         this.baseScale = 1;
24191         this.rotate = 0;
24192         this.baseRotate = 1;
24193         this.dragable = false;
24194         this.pinching = false;
24195         this.mouseX = 0;
24196         this.mouseY = 0;
24197         this.cropData = false;
24198         this.notifyEl.dom.innerHTML = this.emptyText;
24199         
24200     },
24201     
24202     resize : function()
24203     {
24204         if(this.fireEvent('resize', this) != false){
24205             this.setThumbBoxPosition();
24206             this.setCanvasPosition();
24207         }
24208     },
24209     
24210     onFooterButtonClick : function(e, el, o, type)
24211     {
24212         switch (type) {
24213             case 'rotate-left' :
24214                 this.onRotateLeft(e);
24215                 break;
24216             case 'rotate-right' :
24217                 this.onRotateRight(e);
24218                 break;
24219             case 'picture' :
24220                 this.beforeSelectFile(e);
24221                 break;
24222             case 'trash' :
24223                 this.trash(e);
24224                 break;
24225             case 'crop' :
24226                 this.crop(e);
24227                 break;
24228             case 'download' :
24229                 this.download(e);
24230                 break;
24231             default :
24232                 break;
24233         }
24234         
24235         this.fireEvent('footerbuttonclick', this, type);
24236     },
24237     
24238     beforeSelectFile : function(e)
24239     {
24240         this.fireEvent('beforeselectfile', this);
24241     },
24242     
24243     trash : function(e)
24244     {
24245         this.fireEvent('trash', this);
24246     },
24247     
24248     download : function(e)
24249     {
24250         this.fireEvent('download', this);
24251     },
24252     
24253     loadCanvas : function(src)
24254     {   
24255         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24256             
24257             this.reset();
24258             
24259             this.imageEl = document.createElement('img');
24260             
24261             var _this = this;
24262             
24263             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24264             
24265             this.imageEl.src = src;
24266         }
24267     },
24268     
24269     onLoadCanvas : function()
24270     {   
24271         this.bodyEl.un('click', this.beforeSelectFile, this);
24272         
24273         this.notifyEl.hide();
24274         this.thumbEl.show();
24275         this.footerEl.show();
24276         
24277         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24278         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24279         
24280         this.setThumbBoxPosition();
24281         this.baseRotateLevel();
24282         this.baseScaleLevel();
24283         
24284         this.draw();
24285         
24286         this.resize();
24287         
24288         this.canvasLoaded = true;
24289         
24290     },
24291     
24292     setCanvasPosition : function()
24293     {   
24294         if(!this.canvasEl){
24295             return;
24296         }
24297         
24298         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24299         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24300         
24301         this.previewEl.setLeft(pw);
24302         this.previewEl.setTop(ph);
24303         
24304     },
24305     
24306     onMouseDown : function(e)
24307     {   
24308         e.stopEvent();
24309         
24310         this.dragable = true;
24311         this.pinching = false;
24312         
24313         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24314         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24315         
24316     },
24317     
24318     onMouseMove : function(e)
24319     {   
24320         e.stopEvent();
24321         
24322         if(!this.canvasLoaded){
24323             return;
24324         }
24325         
24326         if (!this.dragable){
24327             return;
24328         }
24329         
24330         var minX = Math.ceil(this.thumbEl.getLeft(true));
24331         var minY = Math.ceil(this.thumbEl.getTop(true));
24332         
24333         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24334         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24335         
24336         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24337         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24338         
24339         x = x - this.mouseX;
24340         y = y - this.mouseY;
24341         
24342         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24343         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24344         
24345         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24346         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24347         
24348         this.previewEl.setLeft(bgX);
24349         this.previewEl.setTop(bgY);
24350         
24351         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24352         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24353     },
24354     
24355     onMouseUp : function(e)
24356     {   
24357         e.stopEvent();
24358         
24359         this.dragable = false;
24360     },
24361     
24362     onMouseWheel : function(e)
24363     {   
24364         e.stopEvent();
24365         
24366         this.startScale = this.scale;
24367         
24368         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24369         
24370         if(!this.zoomable()){
24371             this.scale = this.startScale;
24372             return;
24373         }
24374         
24375         this.draw();
24376         
24377         return;
24378     },
24379     
24380     zoomable : function()
24381     {
24382         var minScale = this.thumbEl.getWidth() / this.minWidth;
24383         
24384         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24385         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24386         
24387         if(
24388                 (this.rotate == 0 || this.rotate == 180) && 
24389                 (
24390                     width / minScale < this.minWidth || 
24391                     width / minScale > this.imageEl.OriginWidth || 
24392                     height / minScale < this.minHeight || 
24393                     height / minScale > this.imageEl.OriginHeight
24394                 )
24395         ){
24396             return false;
24397         }
24398         
24399         if(
24400                 (this.rotate == 90 || this.rotate == 270) && 
24401                 (
24402                     width / minScale < this.minHeight || 
24403                     width / minScale > this.imageEl.OriginWidth || 
24404                     height / minScale < this.minWidth || 
24405                     height / minScale > this.imageEl.OriginHeight
24406                 )
24407         ){
24408             return false;
24409         }
24410         
24411         return true;
24412         
24413     },
24414     
24415     onRotateLeft : function(e)
24416     {   
24417         var minScale = this.thumbEl.getWidth() / this.minWidth;
24418         
24419         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24420             
24421             var bw = this.canvasEl.width / this.getScaleLevel();
24422             var bh = this.canvasEl.height / this.getScaleLevel();
24423             
24424             this.startScale = this.scale;
24425             
24426             while (this.getScaleLevel() < minScale){
24427             
24428                 this.scale = this.scale + 1;
24429                 
24430                 if(!this.zoomable()){
24431                     break;
24432                 }
24433                 
24434                 if(
24435                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24436                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
24437                 ){
24438                     continue;
24439                 }
24440                 
24441                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24442
24443                 this.draw();
24444                 
24445                 return;
24446             }
24447             
24448             this.scale = this.startScale;
24449             
24450             this.onRotateFail();
24451             
24452             return false;
24453         }
24454         
24455         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24456
24457         this.draw();
24458         
24459     },
24460     
24461     onRotateRight : function(e)
24462     {
24463         var minScale = this.thumbEl.getWidth() / this.minWidth;
24464         
24465         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24466             
24467             var bw = this.canvasEl.width / this.getScaleLevel();
24468             var bh = this.canvasEl.height / this.getScaleLevel();
24469             
24470             this.startScale = this.scale;
24471             
24472             while (this.getScaleLevel() < minScale){
24473             
24474                 this.scale = this.scale + 1;
24475                 
24476                 if(!this.zoomable()){
24477                     break;
24478                 }
24479                 
24480                 if(
24481                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24482                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
24483                 ){
24484                     continue;
24485                 }
24486                 
24487                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24488
24489                 this.draw();
24490                 
24491                 return;
24492             }
24493             
24494             this.scale = this.startScale;
24495             
24496             this.onRotateFail();
24497             
24498             return false;
24499         }
24500         
24501         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24502
24503         this.draw();
24504     },
24505     
24506     onRotateFail : function()
24507     {
24508         this.errorEl.show(true);
24509         
24510         var _this = this;
24511         
24512         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24513     },
24514     
24515     draw : function()
24516     {
24517         this.previewEl.dom.innerHTML = '';
24518         
24519         var canvasEl = document.createElement("canvas");
24520         
24521         var contextEl = canvasEl.getContext("2d");
24522         
24523         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24524         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24525         var center = this.imageEl.OriginWidth / 2;
24526         
24527         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24528             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24529             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24530             center = this.imageEl.OriginHeight / 2;
24531         }
24532         
24533         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24534         
24535         contextEl.translate(center, center);
24536         contextEl.rotate(this.rotate * Math.PI / 180);
24537
24538         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24539         
24540         this.canvasEl = document.createElement("canvas");
24541         
24542         this.contextEl = this.canvasEl.getContext("2d");
24543         
24544         switch (this.rotate) {
24545             case 0 :
24546                 
24547                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24548                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24549                 
24550                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24551                 
24552                 break;
24553             case 90 : 
24554                 
24555                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24556                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24557                 
24558                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24559                     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);
24560                     break;
24561                 }
24562                 
24563                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24564                 
24565                 break;
24566             case 180 :
24567                 
24568                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24569                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24570                 
24571                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24572                     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);
24573                     break;
24574                 }
24575                 
24576                 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);
24577                 
24578                 break;
24579             case 270 :
24580                 
24581                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24582                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24583         
24584                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24585                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24586                     break;
24587                 }
24588                 
24589                 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);
24590                 
24591                 break;
24592             default : 
24593                 break;
24594         }
24595         
24596         this.previewEl.appendChild(this.canvasEl);
24597         
24598         this.setCanvasPosition();
24599     },
24600     
24601     crop : function()
24602     {
24603         if(!this.canvasLoaded){
24604             return;
24605         }
24606         var canvas = document.createElement("canvas");
24607         
24608         var context = canvas.getContext("2d");
24609         
24610         canvas.width = this.minWidth;
24611         canvas.height = this.minHeight;
24612         
24613         var cropWidth = this.thumbEl.getWidth();
24614         var cropHeight = this.thumbEl.getHeight();
24615         
24616         var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24617         var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24618         
24619         if(this.canvasEl.width - cropWidth < x){
24620             x = this.canvasEl.width - cropWidth;
24621         }
24622         
24623         if(this.canvasEl.height - cropHeight < y){
24624             y = this.canvasEl.height - cropHeight;
24625         }
24626         
24627         x = x < 0 ? 0 : x;
24628         y = y < 0 ? 0 : y;
24629         
24630         context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24631         
24632         this.cropData = canvas.toDataURL(this.cropType);
24633         
24634         this.fireEvent('crop', this, this.cropData);
24635         
24636     },
24637     
24638     setThumbBoxSize : function()
24639     {
24640         var height = 300;
24641         var width = Math.ceil(this.minWidth * height / this.minHeight);
24642         
24643         if(this.minWidth > this.minHeight){
24644             width = 300;
24645             height = Math.ceil(this.minHeight * width / this.minWidth);
24646         }
24647         
24648         this.thumbEl.setStyle({
24649             width : width + 'px',
24650             height : height + 'px'
24651         });
24652
24653         return;
24654             
24655     },
24656     
24657     setThumbBoxPosition : function()
24658     {
24659         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24660         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24661         
24662         this.thumbEl.setLeft(x);
24663         this.thumbEl.setTop(y);
24664         
24665     },
24666     
24667     baseRotateLevel : function()
24668     {
24669         this.baseRotate = 1;
24670         
24671         if(
24672                 typeof(this.exif) != 'undefined' &&
24673                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24674                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24675         ){
24676             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24677         }
24678         
24679         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24680         
24681     },
24682     
24683     baseScaleLevel : function()
24684     {
24685         var width, height;
24686         
24687         if(this.baseRotate == 6 || this.baseRotate == 8){
24688             
24689             width = this.thumbEl.getHeight();
24690             this.baseScale = height / this.imageEl.OriginHeight;
24691             
24692             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24693                 height = this.thumbEl.getWidth();
24694                 this.baseScale = height / this.imageEl.OriginHeight;
24695             }
24696             
24697             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24698                 height = this.thumbEl.getWidth();
24699                 this.baseScale = height / this.imageEl.OriginHeight;
24700                 
24701                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24702                     width = this.thumbEl.getHeight();
24703                     this.baseScale = width / this.imageEl.OriginWidth;
24704                 }
24705             }
24706             
24707             return;
24708         }
24709         
24710         width = this.thumbEl.getWidth();
24711         this.baseScale = width / this.imageEl.OriginWidth;
24712         
24713         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24714             height = this.thumbEl.getHeight();
24715             this.baseScale = height / this.imageEl.OriginHeight;
24716         }
24717         
24718         
24719         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24720             
24721             height = this.thumbEl.getHeight();
24722             this.baseScale = height / this.imageEl.OriginHeight;
24723             
24724             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24725                 width = this.thumbEl.getWidth();
24726                 this.baseScale = width / this.imageEl.OriginWidth;
24727             }
24728             
24729         }
24730         
24731         return;
24732     },
24733     
24734     getScaleLevel : function()
24735     {
24736         return this.baseScale * Math.pow(1.1, this.scale);
24737     },
24738     
24739     onTouchStart : function(e)
24740     {
24741         if(!this.canvasLoaded){
24742             this.beforeSelectFile(e);
24743             return;
24744         }
24745         
24746         var touches = e.browserEvent.touches;
24747         
24748         if(!touches){
24749             return;
24750         }
24751         
24752         if(touches.length == 1){
24753             this.onMouseDown(e);
24754             return;
24755         }
24756         
24757         if(touches.length != 2){
24758             return;
24759         }
24760         
24761         var coords = [];
24762         
24763         for(var i = 0, finger; finger = touches[i]; i++){
24764             coords.push(finger.pageX, finger.pageY);
24765         }
24766         
24767         var x = Math.pow(coords[0] - coords[2], 2);
24768         var y = Math.pow(coords[1] - coords[3], 2);
24769         
24770         this.startDistance = Math.sqrt(x + y);
24771         
24772         this.startScale = this.scale;
24773         
24774         this.pinching = true;
24775         this.dragable = false;
24776         
24777     },
24778     
24779     onTouchMove : function(e)
24780     {
24781         if(!this.pinching && !this.dragable){
24782             return;
24783         }
24784         
24785         var touches = e.browserEvent.touches;
24786         
24787         if(!touches){
24788             return;
24789         }
24790         
24791         if(this.dragable){
24792             this.onMouseMove(e);
24793             return;
24794         }
24795         
24796         var coords = [];
24797         
24798         for(var i = 0, finger; finger = touches[i]; i++){
24799             coords.push(finger.pageX, finger.pageY);
24800         }
24801         
24802         var x = Math.pow(coords[0] - coords[2], 2);
24803         var y = Math.pow(coords[1] - coords[3], 2);
24804         
24805         this.endDistance = Math.sqrt(x + y);
24806         
24807         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24808         
24809         if(!this.zoomable()){
24810             this.scale = this.startScale;
24811             return;
24812         }
24813         
24814         this.draw();
24815         
24816     },
24817     
24818     onTouchEnd : function(e)
24819     {
24820         this.pinching = false;
24821         this.dragable = false;
24822         
24823     },
24824     
24825     prepare : function(input)
24826     {        
24827         this.file = false;
24828         this.exif = {};
24829         
24830         if(typeof(input) === 'string'){
24831             this.loadCanvas(input);
24832             return;
24833         }
24834         
24835         if(!input.files || !input.files[0] || !this.urlAPI){
24836             return;
24837         }
24838         
24839         this.file = input.files[0];
24840         this.cropType = this.file.type;
24841         
24842         var _this = this;
24843         
24844         if(this.fireEvent('prepare', this, this.file) != false){
24845             
24846             var reader = new FileReader();
24847             
24848             reader.onload = function (e) {
24849                 if (e.target.error) {
24850                     Roo.log(e.target.error);
24851                     return;
24852                 }
24853                 
24854                 var buffer = e.target.result,
24855                     dataView = new DataView(buffer),
24856                     offset = 2,
24857                     maxOffset = dataView.byteLength - 4,
24858                     markerBytes,
24859                     markerLength;
24860                 
24861                 if (dataView.getUint16(0) === 0xffd8) {
24862                     while (offset < maxOffset) {
24863                         markerBytes = dataView.getUint16(offset);
24864                         
24865                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24866                             markerLength = dataView.getUint16(offset + 2) + 2;
24867                             if (offset + markerLength > dataView.byteLength) {
24868                                 Roo.log('Invalid meta data: Invalid segment size.');
24869                                 break;
24870                             }
24871                             
24872                             if(markerBytes == 0xffe1){
24873                                 _this.parseExifData(
24874                                     dataView,
24875                                     offset,
24876                                     markerLength
24877                                 );
24878                             }
24879                             
24880                             offset += markerLength;
24881                             
24882                             continue;
24883                         }
24884                         
24885                         break;
24886                     }
24887                     
24888                 }
24889                 
24890                 var url = _this.urlAPI.createObjectURL(_this.file);
24891                 
24892                 _this.loadCanvas(url);
24893                 
24894                 return;
24895             }
24896             
24897             reader.readAsArrayBuffer(this.file);
24898             
24899         }
24900         
24901     },
24902     
24903     parseExifData : function(dataView, offset, length)
24904     {
24905         var tiffOffset = offset + 10,
24906             littleEndian,
24907             dirOffset;
24908     
24909         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24910             // No Exif data, might be XMP data instead
24911             return;
24912         }
24913         
24914         // Check for the ASCII code for "Exif" (0x45786966):
24915         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24916             // No Exif data, might be XMP data instead
24917             return;
24918         }
24919         if (tiffOffset + 8 > dataView.byteLength) {
24920             Roo.log('Invalid Exif data: Invalid segment size.');
24921             return;
24922         }
24923         // Check for the two null bytes:
24924         if (dataView.getUint16(offset + 8) !== 0x0000) {
24925             Roo.log('Invalid Exif data: Missing byte alignment offset.');
24926             return;
24927         }
24928         // Check the byte alignment:
24929         switch (dataView.getUint16(tiffOffset)) {
24930         case 0x4949:
24931             littleEndian = true;
24932             break;
24933         case 0x4D4D:
24934             littleEndian = false;
24935             break;
24936         default:
24937             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24938             return;
24939         }
24940         // Check for the TIFF tag marker (0x002A):
24941         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24942             Roo.log('Invalid Exif data: Missing TIFF marker.');
24943             return;
24944         }
24945         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24946         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24947         
24948         this.parseExifTags(
24949             dataView,
24950             tiffOffset,
24951             tiffOffset + dirOffset,
24952             littleEndian
24953         );
24954     },
24955     
24956     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24957     {
24958         var tagsNumber,
24959             dirEndOffset,
24960             i;
24961         if (dirOffset + 6 > dataView.byteLength) {
24962             Roo.log('Invalid Exif data: Invalid directory offset.');
24963             return;
24964         }
24965         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24966         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24967         if (dirEndOffset + 4 > dataView.byteLength) {
24968             Roo.log('Invalid Exif data: Invalid directory size.');
24969             return;
24970         }
24971         for (i = 0; i < tagsNumber; i += 1) {
24972             this.parseExifTag(
24973                 dataView,
24974                 tiffOffset,
24975                 dirOffset + 2 + 12 * i, // tag offset
24976                 littleEndian
24977             );
24978         }
24979         // Return the offset to the next directory:
24980         return dataView.getUint32(dirEndOffset, littleEndian);
24981     },
24982     
24983     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
24984     {
24985         var tag = dataView.getUint16(offset, littleEndian);
24986         
24987         this.exif[tag] = this.getExifValue(
24988             dataView,
24989             tiffOffset,
24990             offset,
24991             dataView.getUint16(offset + 2, littleEndian), // tag type
24992             dataView.getUint32(offset + 4, littleEndian), // tag length
24993             littleEndian
24994         );
24995     },
24996     
24997     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
24998     {
24999         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25000             tagSize,
25001             dataOffset,
25002             values,
25003             i,
25004             str,
25005             c;
25006     
25007         if (!tagType) {
25008             Roo.log('Invalid Exif data: Invalid tag type.');
25009             return;
25010         }
25011         
25012         tagSize = tagType.size * length;
25013         // Determine if the value is contained in the dataOffset bytes,
25014         // or if the value at the dataOffset is a pointer to the actual data:
25015         dataOffset = tagSize > 4 ?
25016                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25017         if (dataOffset + tagSize > dataView.byteLength) {
25018             Roo.log('Invalid Exif data: Invalid data offset.');
25019             return;
25020         }
25021         if (length === 1) {
25022             return tagType.getValue(dataView, dataOffset, littleEndian);
25023         }
25024         values = [];
25025         for (i = 0; i < length; i += 1) {
25026             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25027         }
25028         
25029         if (tagType.ascii) {
25030             str = '';
25031             // Concatenate the chars:
25032             for (i = 0; i < values.length; i += 1) {
25033                 c = values[i];
25034                 // Ignore the terminating NULL byte(s):
25035                 if (c === '\u0000') {
25036                     break;
25037                 }
25038                 str += c;
25039             }
25040             return str;
25041         }
25042         return values;
25043     }
25044     
25045 });
25046
25047 Roo.apply(Roo.bootstrap.UploadCropbox, {
25048     tags : {
25049         'Orientation': 0x0112
25050     },
25051     
25052     Orientation: {
25053             1: 0, //'top-left',
25054 //            2: 'top-right',
25055             3: 180, //'bottom-right',
25056 //            4: 'bottom-left',
25057 //            5: 'left-top',
25058             6: 90, //'right-top',
25059 //            7: 'right-bottom',
25060             8: 270 //'left-bottom'
25061     },
25062     
25063     exifTagTypes : {
25064         // byte, 8-bit unsigned int:
25065         1: {
25066             getValue: function (dataView, dataOffset) {
25067                 return dataView.getUint8(dataOffset);
25068             },
25069             size: 1
25070         },
25071         // ascii, 8-bit byte:
25072         2: {
25073             getValue: function (dataView, dataOffset) {
25074                 return String.fromCharCode(dataView.getUint8(dataOffset));
25075             },
25076             size: 1,
25077             ascii: true
25078         },
25079         // short, 16 bit int:
25080         3: {
25081             getValue: function (dataView, dataOffset, littleEndian) {
25082                 return dataView.getUint16(dataOffset, littleEndian);
25083             },
25084             size: 2
25085         },
25086         // long, 32 bit int:
25087         4: {
25088             getValue: function (dataView, dataOffset, littleEndian) {
25089                 return dataView.getUint32(dataOffset, littleEndian);
25090             },
25091             size: 4
25092         },
25093         // rational = two long values, first is numerator, second is denominator:
25094         5: {
25095             getValue: function (dataView, dataOffset, littleEndian) {
25096                 return dataView.getUint32(dataOffset, littleEndian) /
25097                     dataView.getUint32(dataOffset + 4, littleEndian);
25098             },
25099             size: 8
25100         },
25101         // slong, 32 bit signed int:
25102         9: {
25103             getValue: function (dataView, dataOffset, littleEndian) {
25104                 return dataView.getInt32(dataOffset, littleEndian);
25105             },
25106             size: 4
25107         },
25108         // srational, two slongs, first is numerator, second is denominator:
25109         10: {
25110             getValue: function (dataView, dataOffset, littleEndian) {
25111                 return dataView.getInt32(dataOffset, littleEndian) /
25112                     dataView.getInt32(dataOffset + 4, littleEndian);
25113             },
25114             size: 8
25115         }
25116     },
25117     
25118     footer : {
25119         STANDARD : [
25120             {
25121                 tag : 'div',
25122                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25123                 action : 'rotate-left',
25124                 cn : [
25125                     {
25126                         tag : 'button',
25127                         cls : 'btn btn-default',
25128                         html : '<i class="fa fa-undo"></i>'
25129                     }
25130                 ]
25131             },
25132             {
25133                 tag : 'div',
25134                 cls : 'btn-group roo-upload-cropbox-picture',
25135                 action : 'picture',
25136                 cn : [
25137                     {
25138                         tag : 'button',
25139                         cls : 'btn btn-default',
25140                         html : '<i class="fa fa-picture-o"></i>'
25141                     }
25142                 ]
25143             },
25144             {
25145                 tag : 'div',
25146                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25147                 action : 'rotate-right',
25148                 cn : [
25149                     {
25150                         tag : 'button',
25151                         cls : 'btn btn-default',
25152                         html : '<i class="fa fa-repeat"></i>'
25153                     }
25154                 ]
25155             }
25156         ],
25157         DOCUMENT : [
25158             {
25159                 tag : 'div',
25160                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25161                 action : 'rotate-left',
25162                 cn : [
25163                     {
25164                         tag : 'button',
25165                         cls : 'btn btn-default',
25166                         html : '<i class="fa fa-undo"></i>'
25167                     }
25168                 ]
25169             },
25170             {
25171                 tag : 'div',
25172                 cls : 'btn-group roo-upload-cropbox-download',
25173                 action : 'download',
25174                 cn : [
25175                     {
25176                         tag : 'button',
25177                         cls : 'btn btn-default',
25178                         html : '<i class="fa fa-download"></i>'
25179                     }
25180                 ]
25181             },
25182             {
25183                 tag : 'div',
25184                 cls : 'btn-group roo-upload-cropbox-crop',
25185                 action : 'crop',
25186                 cn : [
25187                     {
25188                         tag : 'button',
25189                         cls : 'btn btn-default',
25190                         html : '<i class="fa fa-crop"></i>'
25191                     }
25192                 ]
25193             },
25194             {
25195                 tag : 'div',
25196                 cls : 'btn-group roo-upload-cropbox-trash',
25197                 action : 'trash',
25198                 cn : [
25199                     {
25200                         tag : 'button',
25201                         cls : 'btn btn-default',
25202                         html : '<i class="fa fa-trash"></i>'
25203                     }
25204                 ]
25205             },
25206             {
25207                 tag : 'div',
25208                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25209                 action : 'rotate-right',
25210                 cn : [
25211                     {
25212                         tag : 'button',
25213                         cls : 'btn btn-default',
25214                         html : '<i class="fa fa-repeat"></i>'
25215                     }
25216                 ]
25217             }
25218         ]
25219     }
25220 });
25221
25222 /*
25223 * Licence: LGPL
25224 */
25225
25226 /**
25227  * @class Roo.bootstrap.DocumentManager
25228  * @extends Roo.bootstrap.Component
25229  * Bootstrap DocumentManager class
25230  * @cfg {String} paramName default 'imageUpload'
25231  * @cfg {String} method default POST
25232  * @cfg {String} url action url
25233  * @cfg {Number} boxes number of boxes default 12
25234  * @cfg {Boolean} multiple multiple upload default true
25235  * @cfg {Number} minWidth default 300
25236  * @cfg {Number} minHeight default 300
25237  * @cfg {Number} thumbSize default 300
25238  * @cfg {String} fieldLabel
25239  * @cfg {Number} labelWidth default 4
25240  * @cfg {String} labelAlign (left|top) default left
25241  * 
25242  * @constructor
25243  * Create a new DocumentManager
25244  * @param {Object} config The config object
25245  */
25246
25247 Roo.bootstrap.DocumentManager = function(config){
25248     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25249     
25250     this.addEvents({
25251         /**
25252          * @event initial
25253          * Fire when initial the DocumentManager
25254          * @param {Roo.bootstrap.DocumentManager} this
25255          */
25256         "initial" : true,
25257         /**
25258          * @event inspect
25259          * inspect selected file
25260          * @param {Roo.bootstrap.DocumentManager} this
25261          * @param {File} file
25262          */
25263         "inspect" : true,
25264         /**
25265          * @event exception
25266          * Fire when xhr load exception
25267          * @param {Roo.bootstrap.DocumentManager} this
25268          * @param {XMLHttpRequest} xhr
25269          */
25270         "exception" : true,
25271         /**
25272          * @event prepare
25273          * prepare the form data
25274          * @param {Roo.bootstrap.DocumentManager} this
25275          * @param {Object} formData
25276          */
25277         "prepare" : true,
25278         /**
25279          * @event remove
25280          * Fire when remove the file
25281          * @param {Roo.bootstrap.DocumentManager} this
25282          * @param {Object} file
25283          */
25284         "remove" : true,
25285         /**
25286          * @event refresh
25287          * Fire after refresh the file
25288          * @param {Roo.bootstrap.DocumentManager} this
25289          */
25290         "refresh" : true,
25291         /**
25292          * @event click
25293          * Fire after click the image
25294          * @param {Roo.bootstrap.DocumentManager} this
25295          * @param {Object} file
25296          */
25297         "click" : true
25298         
25299     });
25300 };
25301
25302 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25303     
25304     boxes : 12,
25305     inputName : '',
25306     minWidth : 300,
25307     minHeight : 300,
25308     thumbSize : 300,
25309     multiple : true,
25310     files : [],
25311     method : 'POST',
25312     url : '',
25313     paramName : 'imageUpload',
25314     fieldLabel : '',
25315     labelWidth : 4,
25316     labelAlign : 'left',
25317     
25318     getAutoCreate : function()
25319     {   
25320         var managerWidget = {
25321             tag : 'div',
25322             cls : 'roo-document-manager',
25323             cn : [
25324                 {
25325                     tag : 'input',
25326                     cls : 'roo-document-manager-selector',
25327                     type : 'file'
25328                 },
25329                 {
25330                     tag : 'div',
25331                     cls : 'roo-document-manager-uploader',
25332                     cn : [
25333                         {
25334                             tag : 'div',
25335                             cls : 'roo-document-manager-upload-btn',
25336                             html : '<i class="fa fa-plus"></i>'
25337                         }
25338                     ]
25339                     
25340                 }
25341             ]
25342         };
25343         
25344         var content = [
25345             {
25346                 tag : 'div',
25347                 cls : 'column col-md-12',
25348                 cn : managerWidget
25349             }
25350         ];
25351         
25352         if(this.fieldLabel.length){
25353             
25354             content = [
25355                 {
25356                     tag : 'div',
25357                     cls : 'column col-md-12',
25358                     html : this.fieldLabel
25359                 },
25360                 {
25361                     tag : 'div',
25362                     cls : 'column col-md-12',
25363                     cn : managerWidget
25364                 }
25365             ];
25366
25367             if(this.labelAlign == 'left'){
25368                 content = [
25369                     {
25370                         tag : 'div',
25371                         cls : 'column col-md-' + this.labelWidth,
25372                         html : this.fieldLabel
25373                     },
25374                     {
25375                         tag : 'div',
25376                         cls : 'column col-md-' + (12 - this.labelWidth),
25377                         cn : managerWidget
25378                     }
25379                 ];
25380                 
25381             }
25382         }
25383         
25384         var cfg = {
25385             tag : 'div',
25386             cls : 'row clearfix',
25387             cn : content
25388         };
25389         
25390         return cfg;
25391         
25392     },
25393     
25394     initEvents : function()
25395     {
25396         this.managerEl = this.el.select('.roo-document-manager', true).first();
25397         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25398         
25399         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25400         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25401         this.selectorEl.hide();
25402         
25403         if(this.multiple){
25404             this.selectorEl.attr('multiple', 'multiple');
25405         }
25406         
25407         this.selectorEl.on('change', this.onSelect, this);
25408         
25409         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25410         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25411         
25412         this.uploader.on('click', this.onUpload, this);
25413         
25414         var _this = this;
25415         
25416         window.addEventListener("resize", function() { _this.refresh(); } );
25417         
25418         this.fireEvent('initial', this);
25419     },
25420     
25421     onUpload : function(e)
25422     {
25423         e.preventDefault();
25424         
25425         this.selectorEl.dom.click();
25426         
25427     },
25428     
25429     onSelect : function(e)
25430     {
25431         e.preventDefault();
25432         
25433         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25434             return;
25435         }
25436         
25437         Roo.each(this.selectorEl.dom.files, function(file){
25438             if(this.fireEvent('inspect', this, file) != false){
25439                 this.files.push(file);
25440             }
25441         }, this);
25442         
25443         this.process();
25444         
25445     },
25446     
25447     process : function()
25448     {
25449         this.selectorEl.dom.value = '';
25450         
25451         if(!this.files.length){
25452             return;
25453         }
25454         
25455         if(this.files.length > this.boxes){
25456             this.files = this.files.slice(0, this.boxes);
25457         }
25458         
25459         var xhr = new XMLHttpRequest();
25460         
25461         Roo.each(this.files, function(file, index){
25462             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25463                 return;
25464             }
25465             
25466             file.xhr = xhr;
25467             
25468             this.managerEl.createChild({
25469                 tag : 'div',
25470                 cls : 'roo-document-manager-loading',
25471                 cn : [
25472                     {
25473                         tag : 'div',
25474                         tooltip : file.name,
25475                         cls : 'roo-document-manager-thumb',
25476                         html : '<i class="fa fa-spinner fa-pulse"></i>'
25477                     }
25478                 ]
25479
25480             });
25481             
25482         }, this);
25483         
25484         if(this.files.length > this.boxes - 1 ){
25485             this.uploader.hide();
25486         }
25487         
25488         var headers = {
25489             "Accept": "application/json",
25490             "Cache-Control": "no-cache",
25491             "X-Requested-With": "XMLHttpRequest"
25492         };
25493         
25494         xhr.open(this.method, this.url, true);
25495         
25496         for (var headerName in headers) {
25497             var headerValue = headers[headerName];
25498             if (headerValue) {
25499                 xhr.setRequestHeader(headerName, headerValue);
25500             }
25501         }
25502         
25503         var _this = this;
25504         
25505         xhr.onload = function()
25506         {
25507             _this.xhrOnLoad(xhr);
25508         }
25509         
25510         xhr.onerror = function()
25511         {
25512             _this.xhrOnError(xhr);
25513         }
25514         
25515         var formData = new FormData();
25516
25517         formData.append('returnHTML', 'NO');
25518         
25519         Roo.each(this.files, function(file, index){
25520             
25521             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25522                 return;
25523             }
25524             
25525             formData.append(this.getParamName(index), file, file.name);
25526             
25527         }, this);
25528         
25529         if(this.fireEvent('prepare', this, formData) != false){
25530             xhr.send(formData);
25531         };
25532         
25533     },
25534     
25535     getParamName : function(i)
25536     {
25537         if(!this.multiple){
25538             return this.paramName;
25539         }
25540         
25541         return this.paramName + "_" + i;
25542     },
25543     
25544     refresh : function()
25545     {
25546         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25547             el.remove();
25548         }, this);
25549         
25550         
25551         var files = [];
25552         
25553         Roo.each(this.files, function(file){
25554             
25555             if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25556                 return;
25557             }
25558             
25559             if(file.target){
25560                 files.push(file);
25561                 return;
25562             }
25563             
25564             var previewEl = this.managerEl.createChild({
25565                 tag : 'div',
25566                 cls : 'roo-document-manager-preview',
25567                 cn : [
25568                     {
25569                         tag : 'div',
25570                         tooltip : file.filename,
25571                         cls : 'roo-document-manager-thumb',
25572                         html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25573                     },
25574                     {
25575                         tag : 'button',
25576                         cls : 'close',
25577                         html : 'x'
25578                     }
25579                 ]
25580             });
25581             
25582             var close = previewEl.select('button.close', true).first();
25583             
25584             close.on('click', this.onRemove, this, file);
25585             
25586             file.target = previewEl;
25587             
25588             var image = previewEl.select('img', true).first();
25589             
25590             image.on('click', this.onClick, this, file);
25591             
25592             files.push(file);
25593             
25594             return;
25595             
25596         }, this);
25597         
25598         this.files = files;
25599         
25600         this.uploader.show();
25601         
25602         if(this.files.length > this.boxes - 1){
25603             this.uploader.hide();
25604         }
25605         
25606         Roo.isTouch ? this.closable(false) : this.closable(true);
25607         
25608         this.fireEvent('refresh', this);
25609     },
25610     
25611     onRemove : function(e, el, o)
25612     {
25613         e.preventDefault();
25614         
25615         this.fireEvent('remove', this, o);
25616         
25617     },
25618     
25619     remove : function(o)
25620     {
25621         var files = [];
25622         
25623         Roo.each(this.files, function(file){
25624             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25625                 files.push(file);
25626                 return;
25627             }
25628
25629             o.target.remove();
25630
25631         }, this);
25632         
25633         this.files = files;
25634         
25635         this.refresh();
25636     },
25637     
25638     onClick : function(e, el, o)
25639     {
25640         e.preventDefault();
25641         
25642         this.fireEvent('click', this, o);
25643         
25644     },
25645     
25646     closable : function(closable)
25647     {
25648         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25649             
25650             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25651             
25652             if(closable){
25653                 el.show();
25654                 return;
25655             }
25656             
25657             el.hide();
25658             
25659         }, this);
25660     },
25661     
25662     xhrOnLoad : function(xhr)
25663     {
25664         if (xhr.readyState !== 4) {
25665             this.refresh();
25666             this.fireEvent('exception', this, xhr);
25667             return;
25668         }
25669
25670         var response = Roo.decode(xhr.responseText);
25671         
25672         if(!response.success){
25673             this.refresh();
25674             this.fireEvent('exception', this, xhr);
25675             return;
25676         }
25677         
25678         var i = 0;
25679         
25680         Roo.each(this.files, function(file, index){
25681             
25682             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25683                 return;
25684             }
25685             
25686             this.files[index] = response.data[i];
25687             i++;
25688             
25689             return;
25690             
25691         }, this);
25692         
25693         this.refresh();
25694         
25695     },
25696     
25697     xhrOnError : function()
25698     {
25699         Roo.log('xhr on error');
25700         
25701         var response = Roo.decode(xhr.responseText);
25702           
25703         Roo.log(response);
25704     }
25705     
25706     
25707     
25708 });
25709 /*
25710 * Licence: LGPL
25711 */
25712
25713 /**
25714  * @class Roo.bootstrap.DocumentViewer
25715  * @extends Roo.bootstrap.Component
25716  * Bootstrap DocumentViewer class
25717  * 
25718  * @constructor
25719  * Create a new DocumentViewer
25720  * @param {Object} config The config object
25721  */
25722
25723 Roo.bootstrap.DocumentViewer = function(config){
25724     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25725     
25726     this.addEvents({
25727         /**
25728          * @event initial
25729          * Fire after initEvent
25730          * @param {Roo.bootstrap.DocumentViewer} this
25731          */
25732         "initial" : true,
25733         /**
25734          * @event click
25735          * Fire after click
25736          * @param {Roo.bootstrap.DocumentViewer} this
25737          */
25738         "click" : true,
25739         /**
25740          * @event trash
25741          * Fire after trash button
25742          * @param {Roo.bootstrap.DocumentViewer} this
25743          */
25744         "trash" : true
25745         
25746     });
25747 };
25748
25749 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
25750     
25751     getAutoCreate : function()
25752     {
25753         var cfg = {
25754             tag : 'div',
25755             cls : 'roo-document-viewer',
25756             cn : [
25757                 {
25758                     tag : 'div',
25759                     cls : 'roo-document-viewer-body',
25760                     cn : [
25761                         {
25762                             tag : 'div',
25763                             cls : 'roo-document-viewer-thumb',
25764                             cn : [
25765                                 {
25766                                     tag : 'img',
25767                                     cls : 'roo-document-viewer-image'
25768                                 }
25769                             ]
25770                         }
25771                     ]
25772                 },
25773                 {
25774                     tag : 'div',
25775                     cls : 'roo-document-viewer-footer',
25776                     cn : {
25777                         tag : 'div',
25778                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25779                         cn : [
25780                             {
25781                                 tag : 'div',
25782                                 cls : 'btn-group',
25783                                 cn : [
25784                                     {
25785                                         tag : 'button',
25786                                         cls : 'btn btn-default roo-document-viewer-trash',
25787                                         html : '<i class="fa fa-trash"></i>'
25788                                     }
25789                                 ]
25790                             }
25791                         ]
25792                     }
25793                 }
25794             ]
25795         };
25796         
25797         return cfg;
25798     },
25799     
25800     initEvents : function()
25801     {
25802         
25803         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25804         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25805         
25806         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25807         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25808         
25809         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25810         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25811         
25812         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25813         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25814         
25815         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25816         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25817         
25818         this.bodyEl.on('click', this.onClick, this);
25819         
25820         this.trashBtn.on('click', this.onTrash, this);
25821         
25822     },
25823     
25824     initial : function()
25825     {
25826 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25827         
25828         
25829         this.fireEvent('initial', this);
25830         
25831     },
25832     
25833     onClick : function(e)
25834     {
25835         e.preventDefault();
25836         
25837         this.fireEvent('click', this);
25838     },
25839     
25840     onTrash : function(e)
25841     {
25842         e.preventDefault();
25843         
25844         this.fireEvent('trash', this);
25845     }
25846     
25847 });