027190f8a7c5b3ba0070bb34ba7658f41672a105
[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  * 
7486  * @constructor
7487  * Create a new Input
7488  * @param {Object} config The config object
7489  */
7490
7491 Roo.bootstrap.Input = function(config){
7492     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7493    
7494         this.addEvents({
7495             /**
7496              * @event focus
7497              * Fires when this field receives input focus.
7498              * @param {Roo.form.Field} this
7499              */
7500             focus : true,
7501             /**
7502              * @event blur
7503              * Fires when this field loses input focus.
7504              * @param {Roo.form.Field} this
7505              */
7506             blur : true,
7507             /**
7508              * @event specialkey
7509              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7510              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7511              * @param {Roo.form.Field} this
7512              * @param {Roo.EventObject} e The event object
7513              */
7514             specialkey : true,
7515             /**
7516              * @event change
7517              * Fires just before the field blurs if the field value has changed.
7518              * @param {Roo.form.Field} this
7519              * @param {Mixed} newValue The new value
7520              * @param {Mixed} oldValue The original value
7521              */
7522             change : true,
7523             /**
7524              * @event invalid
7525              * Fires after the field has been marked as invalid.
7526              * @param {Roo.form.Field} this
7527              * @param {String} msg The validation message
7528              */
7529             invalid : true,
7530             /**
7531              * @event valid
7532              * Fires after the field has been validated with no errors.
7533              * @param {Roo.form.Field} this
7534              */
7535             valid : true,
7536              /**
7537              * @event keyup
7538              * Fires after the key up
7539              * @param {Roo.form.Field} this
7540              * @param {Roo.EventObject}  e The event Object
7541              */
7542             keyup : true
7543         });
7544 };
7545
7546 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7547      /**
7548      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7549       automatic validation (defaults to "keyup").
7550      */
7551     validationEvent : "keyup",
7552      /**
7553      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7554      */
7555     validateOnBlur : true,
7556     /**
7557      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7558      */
7559     validationDelay : 250,
7560      /**
7561      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7562      */
7563     focusClass : "x-form-focus",  // not needed???
7564     
7565        
7566     /**
7567      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7568      */
7569     invalidClass : "has-warning",
7570     
7571     /**
7572      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7573      */
7574     validClass : "has-success",
7575     
7576     /**
7577      * @cfg {Boolean} hasFeedback (true|false) default true
7578      */
7579     hasFeedback : true,
7580     
7581     /**
7582      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7583      */
7584     invalidFeedbackClass : "glyphicon-warning-sign",
7585     
7586     /**
7587      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7588      */
7589     validFeedbackClass : "glyphicon-ok",
7590     
7591     /**
7592      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7593      */
7594     selectOnFocus : false,
7595     
7596      /**
7597      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7598      */
7599     maskRe : null,
7600        /**
7601      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7602      */
7603     vtype : null,
7604     
7605       /**
7606      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7607      */
7608     disableKeyFilter : false,
7609     
7610        /**
7611      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7612      */
7613     disabled : false,
7614      /**
7615      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7616      */
7617     allowBlank : true,
7618     /**
7619      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7620      */
7621     blankText : "This field is required",
7622     
7623      /**
7624      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7625      */
7626     minLength : 0,
7627     /**
7628      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7629      */
7630     maxLength : Number.MAX_VALUE,
7631     /**
7632      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7633      */
7634     minLengthText : "The minimum length for this field is {0}",
7635     /**
7636      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7637      */
7638     maxLengthText : "The maximum length for this field is {0}",
7639   
7640     
7641     /**
7642      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7643      * If available, this function will be called only after the basic validators all return true, and will be passed the
7644      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7645      */
7646     validator : null,
7647     /**
7648      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7649      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7650      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7651      */
7652     regex : null,
7653     /**
7654      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7655      */
7656     regexText : "",
7657     
7658     autocomplete: false,
7659     
7660     
7661     fieldLabel : '',
7662     inputType : 'text',
7663     
7664     name : false,
7665     placeholder: false,
7666     before : false,
7667     after : false,
7668     size : false,
7669     hasFocus : false,
7670     preventMark: false,
7671     isFormField : true,
7672     value : '',
7673     labelWidth : 2,
7674     labelAlign : false,
7675     readOnly : false,
7676     align : false,
7677     formatedValue : false,
7678     forceFeedback : false,
7679     
7680     parentLabelAlign : function()
7681     {
7682         var parent = this;
7683         while (parent.parent()) {
7684             parent = parent.parent();
7685             if (typeof(parent.labelAlign) !='undefined') {
7686                 return parent.labelAlign;
7687             }
7688         }
7689         return 'left';
7690         
7691     },
7692     
7693     getAutoCreate : function(){
7694         
7695         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7696         
7697         var id = Roo.id();
7698         
7699         var cfg = {};
7700         
7701         if(this.inputType != 'hidden'){
7702             cfg.cls = 'form-group' //input-group
7703         }
7704         
7705         var input =  {
7706             tag: 'input',
7707             id : id,
7708             type : this.inputType,
7709             value : this.value,
7710             cls : 'form-control',
7711             placeholder : this.placeholder || '',
7712             autocomplete : this.autocomplete || 'new-password'
7713         };
7714         
7715         
7716         if(this.align){
7717             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7718         }
7719         
7720         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7721             input.maxLength = this.maxLength;
7722         }
7723         
7724         if (this.disabled) {
7725             input.disabled=true;
7726         }
7727         
7728         if (this.readOnly) {
7729             input.readonly=true;
7730         }
7731         
7732         if (this.name) {
7733             input.name = this.name;
7734         }
7735         if (this.size) {
7736             input.cls += ' input-' + this.size;
7737         }
7738         var settings=this;
7739         ['xs','sm','md','lg'].map(function(size){
7740             if (settings[size]) {
7741                 cfg.cls += ' col-' + size + '-' + settings[size];
7742             }
7743         });
7744         
7745         var inputblock = input;
7746         
7747         var feedback = {
7748             tag: 'span',
7749             cls: 'glyphicon form-control-feedback'
7750         };
7751             
7752         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7753             
7754             inputblock = {
7755                 cls : 'has-feedback',
7756                 cn :  [
7757                     input,
7758                     feedback
7759                 ] 
7760             };  
7761         }
7762         
7763         if (this.before || this.after) {
7764             
7765             inputblock = {
7766                 cls : 'input-group',
7767                 cn :  [] 
7768             };
7769             
7770             if (this.before && typeof(this.before) == 'string') {
7771                 
7772                 inputblock.cn.push({
7773                     tag :'span',
7774                     cls : 'roo-input-before input-group-addon',
7775                     html : this.before
7776                 });
7777             }
7778             if (this.before && typeof(this.before) == 'object') {
7779                 this.before = Roo.factory(this.before);
7780                 Roo.log(this.before);
7781                 inputblock.cn.push({
7782                     tag :'span',
7783                     cls : 'roo-input-before input-group-' +
7784                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7785                 });
7786             }
7787             
7788             inputblock.cn.push(input);
7789             
7790             if (this.after && typeof(this.after) == 'string') {
7791                 inputblock.cn.push({
7792                     tag :'span',
7793                     cls : 'roo-input-after input-group-addon',
7794                     html : this.after
7795                 });
7796             }
7797             if (this.after && typeof(this.after) == 'object') {
7798                 this.after = Roo.factory(this.after);
7799                 Roo.log(this.after);
7800                 inputblock.cn.push({
7801                     tag :'span',
7802                     cls : 'roo-input-after input-group-' +
7803                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7804                 });
7805             }
7806             
7807             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7808                 inputblock.cls += ' has-feedback';
7809                 inputblock.cn.push(feedback);
7810             }
7811         };
7812         
7813         if (align ==='left' && this.fieldLabel.length) {
7814                 Roo.log("left and has label");
7815                 cfg.cn = [
7816                     
7817                     {
7818                         tag: 'label',
7819                         'for' :  id,
7820                         cls : 'control-label col-sm-' + this.labelWidth,
7821                         html : this.fieldLabel
7822                         
7823                     },
7824                     {
7825                         cls : "col-sm-" + (12 - this.labelWidth), 
7826                         cn: [
7827                             inputblock
7828                         ]
7829                     }
7830                     
7831                 ];
7832         } else if ( this.fieldLabel.length) {
7833                 Roo.log(" label");
7834                  cfg.cn = [
7835                    
7836                     {
7837                         tag: 'label',
7838                         //cls : 'input-group-addon',
7839                         html : this.fieldLabel
7840                         
7841                     },
7842                     
7843                     inputblock
7844                     
7845                 ];
7846
7847         } else {
7848             
7849                 Roo.log(" no label && no align");
7850                 cfg.cn = [
7851                     
7852                         inputblock
7853                     
7854                 ];
7855                 
7856                 
7857         };
7858         Roo.log('input-parentType: ' + this.parentType);
7859         
7860         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7861            cfg.cls += ' navbar-form';
7862            Roo.log(cfg);
7863         }
7864         
7865         return cfg;
7866         
7867     },
7868     /**
7869      * return the real input element.
7870      */
7871     inputEl: function ()
7872     {
7873         return this.el.select('input.form-control',true).first();
7874     },
7875     
7876     tooltipEl : function()
7877     {
7878         return this.inputEl();
7879     },
7880     
7881     setDisabled : function(v)
7882     {
7883         var i  = this.inputEl().dom;
7884         if (!v) {
7885             i.removeAttribute('disabled');
7886             return;
7887             
7888         }
7889         i.setAttribute('disabled','true');
7890     },
7891     initEvents : function()
7892     {
7893           
7894         this.inputEl().on("keydown" , this.fireKey,  this);
7895         this.inputEl().on("focus", this.onFocus,  this);
7896         this.inputEl().on("blur", this.onBlur,  this);
7897         
7898         this.inputEl().relayEvent('keyup', this);
7899  
7900         // reference to original value for reset
7901         this.originalValue = this.getValue();
7902         //Roo.form.TextField.superclass.initEvents.call(this);
7903         if(this.validationEvent == 'keyup'){
7904             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7905             this.inputEl().on('keyup', this.filterValidation, this);
7906         }
7907         else if(this.validationEvent !== false){
7908             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7909         }
7910         
7911         if(this.selectOnFocus){
7912             this.on("focus", this.preFocus, this);
7913             
7914         }
7915         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7916             this.inputEl().on("keypress", this.filterKeys, this);
7917         }
7918        /* if(this.grow){
7919             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7920             this.el.on("click", this.autoSize,  this);
7921         }
7922         */
7923         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7924             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7925         }
7926         
7927         if (typeof(this.before) == 'object') {
7928             this.before.render(this.el.select('.roo-input-before',true).first());
7929         }
7930         if (typeof(this.after) == 'object') {
7931             this.after.render(this.el.select('.roo-input-after',true).first());
7932         }
7933         
7934         
7935     },
7936     filterValidation : function(e){
7937         if(!e.isNavKeyPress()){
7938             this.validationTask.delay(this.validationDelay);
7939         }
7940     },
7941      /**
7942      * Validates the field value
7943      * @return {Boolean} True if the value is valid, else false
7944      */
7945     validate : function(){
7946         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7947         if(this.disabled || this.validateValue(this.getRawValue())){
7948             this.markValid();
7949             return true;
7950         }
7951         
7952         this.markInvalid();
7953         return false;
7954     },
7955     
7956     
7957     /**
7958      * Validates a value according to the field's validation rules and marks the field as invalid
7959      * if the validation fails
7960      * @param {Mixed} value The value to validate
7961      * @return {Boolean} True if the value is valid, else false
7962      */
7963     validateValue : function(value){
7964         if(value.length < 1)  { // if it's blank
7965             if(this.allowBlank){
7966                 return true;
7967             }
7968             return false;
7969         }
7970         
7971         if(value.length < this.minLength){
7972             return false;
7973         }
7974         if(value.length > this.maxLength){
7975             return false;
7976         }
7977         if(this.vtype){
7978             var vt = Roo.form.VTypes;
7979             if(!vt[this.vtype](value, this)){
7980                 return false;
7981             }
7982         }
7983         if(typeof this.validator == "function"){
7984             var msg = this.validator(value);
7985             if(msg !== true){
7986                 return false;
7987             }
7988         }
7989         
7990         if(this.regex && !this.regex.test(value)){
7991             return false;
7992         }
7993         
7994         return true;
7995     },
7996
7997     
7998     
7999      // private
8000     fireKey : function(e){
8001         //Roo.log('field ' + e.getKey());
8002         if(e.isNavKeyPress()){
8003             this.fireEvent("specialkey", this, e);
8004         }
8005     },
8006     focus : function (selectText){
8007         if(this.rendered){
8008             this.inputEl().focus();
8009             if(selectText === true){
8010                 this.inputEl().dom.select();
8011             }
8012         }
8013         return this;
8014     } ,
8015     
8016     onFocus : function(){
8017         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8018            // this.el.addClass(this.focusClass);
8019         }
8020         if(!this.hasFocus){
8021             this.hasFocus = true;
8022             this.startValue = this.getValue();
8023             this.fireEvent("focus", this);
8024         }
8025     },
8026     
8027     beforeBlur : Roo.emptyFn,
8028
8029     
8030     // private
8031     onBlur : function(){
8032         this.beforeBlur();
8033         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8034             //this.el.removeClass(this.focusClass);
8035         }
8036         this.hasFocus = false;
8037         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8038             this.validate();
8039         }
8040         var v = this.getValue();
8041         if(String(v) !== String(this.startValue)){
8042             this.fireEvent('change', this, v, this.startValue);
8043         }
8044         this.fireEvent("blur", this);
8045     },
8046     
8047     /**
8048      * Resets the current field value to the originally loaded value and clears any validation messages
8049      */
8050     reset : function(){
8051         this.setValue(this.originalValue);
8052         this.validate();
8053     },
8054      /**
8055      * Returns the name of the field
8056      * @return {Mixed} name The name field
8057      */
8058     getName: function(){
8059         return this.name;
8060     },
8061      /**
8062      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8063      * @return {Mixed} value The field value
8064      */
8065     getValue : function(){
8066         
8067         var v = this.inputEl().getValue();
8068         
8069         return v;
8070     },
8071     /**
8072      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8073      * @return {Mixed} value The field value
8074      */
8075     getRawValue : function(){
8076         var v = this.inputEl().getValue();
8077         
8078         return v;
8079     },
8080     
8081     /**
8082      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8083      * @param {Mixed} value The value to set
8084      */
8085     setRawValue : function(v){
8086         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8087     },
8088     
8089     selectText : function(start, end){
8090         var v = this.getRawValue();
8091         if(v.length > 0){
8092             start = start === undefined ? 0 : start;
8093             end = end === undefined ? v.length : end;
8094             var d = this.inputEl().dom;
8095             if(d.setSelectionRange){
8096                 d.setSelectionRange(start, end);
8097             }else if(d.createTextRange){
8098                 var range = d.createTextRange();
8099                 range.moveStart("character", start);
8100                 range.moveEnd("character", v.length-end);
8101                 range.select();
8102             }
8103         }
8104     },
8105     
8106     /**
8107      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8108      * @param {Mixed} value The value to set
8109      */
8110     setValue : function(v){
8111         this.value = v;
8112         if(this.rendered){
8113             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8114             this.validate();
8115         }
8116     },
8117     
8118     /*
8119     processValue : function(value){
8120         if(this.stripCharsRe){
8121             var newValue = value.replace(this.stripCharsRe, '');
8122             if(newValue !== value){
8123                 this.setRawValue(newValue);
8124                 return newValue;
8125             }
8126         }
8127         return value;
8128     },
8129   */
8130     preFocus : function(){
8131         
8132         if(this.selectOnFocus){
8133             this.inputEl().dom.select();
8134         }
8135     },
8136     filterKeys : function(e){
8137         var k = e.getKey();
8138         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8139             return;
8140         }
8141         var c = e.getCharCode(), cc = String.fromCharCode(c);
8142         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8143             return;
8144         }
8145         if(!this.maskRe.test(cc)){
8146             e.stopEvent();
8147         }
8148     },
8149      /**
8150      * Clear any invalid styles/messages for this field
8151      */
8152     clearInvalid : function(){
8153         
8154         if(!this.el || this.preventMark){ // not rendered
8155             return;
8156         }
8157         this.el.removeClass(this.invalidClass);
8158         
8159         this.fireEvent('valid', this);
8160     },
8161     
8162      /**
8163      * Mark this field as valid
8164      */
8165     markValid : function(){
8166         if(!this.el  || this.preventMark){ // not rendered
8167             return;
8168         }
8169         
8170         this.el.removeClass([this.invalidClass, this.validClass]);
8171         
8172         if(this.disabled || this.allowBlank){
8173             return;
8174         }
8175         
8176         this.el.addClass(this.validClass);
8177         
8178         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8179             
8180             var feedback = this.el.select('.form-control-feedback', true).first();
8181             
8182             if(feedback){
8183                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8184                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8185             }
8186             
8187         }
8188         
8189         this.fireEvent('valid', this);
8190     },
8191     
8192      /**
8193      * Mark this field as invalid
8194      * @param {String} msg The validation message
8195      */
8196     markInvalid : function(msg){
8197         if(!this.el  || this.preventMark){ // not rendered
8198             return;
8199         }
8200         
8201         this.el.removeClass([this.invalidClass, this.validClass]);
8202         
8203         if(this.disabled || this.allowBlank){
8204             return;
8205         }
8206         
8207         this.el.addClass(this.invalidClass);
8208         
8209         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8210             
8211             var feedback = this.el.select('.form-control-feedback', true).first();
8212             
8213             if(feedback){
8214                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8215                 
8216                 if(this.getValue().length){
8217                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8218                 }
8219                 
8220             }
8221             
8222         }
8223         
8224         this.fireEvent('invalid', this, msg);
8225     },
8226     // private
8227     SafariOnKeyDown : function(event)
8228     {
8229         // this is a workaround for a password hang bug on chrome/ webkit.
8230         
8231         var isSelectAll = false;
8232         
8233         if(this.inputEl().dom.selectionEnd > 0){
8234             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8235         }
8236         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8237             event.preventDefault();
8238             this.setValue('');
8239             return;
8240         }
8241         
8242         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8243             
8244             event.preventDefault();
8245             // this is very hacky as keydown always get's upper case.
8246             //
8247             var cc = String.fromCharCode(event.getCharCode());
8248             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8249             
8250         }
8251     },
8252     adjustWidth : function(tag, w){
8253         tag = tag.toLowerCase();
8254         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8255             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8256                 if(tag == 'input'){
8257                     return w + 2;
8258                 }
8259                 if(tag == 'textarea'){
8260                     return w-2;
8261                 }
8262             }else if(Roo.isOpera){
8263                 if(tag == 'input'){
8264                     return w + 2;
8265                 }
8266                 if(tag == 'textarea'){
8267                     return w-2;
8268                 }
8269             }
8270         }
8271         return w;
8272     }
8273     
8274 });
8275
8276  
8277 /*
8278  * - LGPL
8279  *
8280  * Input
8281  * 
8282  */
8283
8284 /**
8285  * @class Roo.bootstrap.TextArea
8286  * @extends Roo.bootstrap.Input
8287  * Bootstrap TextArea class
8288  * @cfg {Number} cols Specifies the visible width of a text area
8289  * @cfg {Number} rows Specifies the visible number of lines in a text area
8290  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8291  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8292  * @cfg {string} html text
8293  * 
8294  * @constructor
8295  * Create a new TextArea
8296  * @param {Object} config The config object
8297  */
8298
8299 Roo.bootstrap.TextArea = function(config){
8300     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8301    
8302 };
8303
8304 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8305      
8306     cols : false,
8307     rows : 5,
8308     readOnly : false,
8309     warp : 'soft',
8310     resize : false,
8311     value: false,
8312     html: false,
8313     
8314     getAutoCreate : function(){
8315         
8316         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8317         
8318         var id = Roo.id();
8319         
8320         var cfg = {};
8321         
8322         var input =  {
8323             tag: 'textarea',
8324             id : id,
8325             warp : this.warp,
8326             rows : this.rows,
8327             value : this.value || '',
8328             html: this.html || '',
8329             cls : 'form-control',
8330             placeholder : this.placeholder || '' 
8331             
8332         };
8333         
8334         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8335             input.maxLength = this.maxLength;
8336         }
8337         
8338         if(this.resize){
8339             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8340         }
8341         
8342         if(this.cols){
8343             input.cols = this.cols;
8344         }
8345         
8346         if (this.readOnly) {
8347             input.readonly = true;
8348         }
8349         
8350         if (this.name) {
8351             input.name = this.name;
8352         }
8353         
8354         if (this.size) {
8355             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8356         }
8357         
8358         var settings=this;
8359         ['xs','sm','md','lg'].map(function(size){
8360             if (settings[size]) {
8361                 cfg.cls += ' col-' + size + '-' + settings[size];
8362             }
8363         });
8364         
8365         var inputblock = input;
8366         
8367         if(this.hasFeedback && !this.allowBlank){
8368             
8369             var feedback = {
8370                 tag: 'span',
8371                 cls: 'glyphicon form-control-feedback'
8372             };
8373
8374             inputblock = {
8375                 cls : 'has-feedback',
8376                 cn :  [
8377                     input,
8378                     feedback
8379                 ] 
8380             };  
8381         }
8382         
8383         
8384         if (this.before || this.after) {
8385             
8386             inputblock = {
8387                 cls : 'input-group',
8388                 cn :  [] 
8389             };
8390             if (this.before) {
8391                 inputblock.cn.push({
8392                     tag :'span',
8393                     cls : 'input-group-addon',
8394                     html : this.before
8395                 });
8396             }
8397             
8398             inputblock.cn.push(input);
8399             
8400             if(this.hasFeedback && !this.allowBlank){
8401                 inputblock.cls += ' has-feedback';
8402                 inputblock.cn.push(feedback);
8403             }
8404             
8405             if (this.after) {
8406                 inputblock.cn.push({
8407                     tag :'span',
8408                     cls : 'input-group-addon',
8409                     html : this.after
8410                 });
8411             }
8412             
8413         }
8414         
8415         if (align ==='left' && this.fieldLabel.length) {
8416                 Roo.log("left and has label");
8417                 cfg.cn = [
8418                     
8419                     {
8420                         tag: 'label',
8421                         'for' :  id,
8422                         cls : 'control-label col-sm-' + this.labelWidth,
8423                         html : this.fieldLabel
8424                         
8425                     },
8426                     {
8427                         cls : "col-sm-" + (12 - this.labelWidth), 
8428                         cn: [
8429                             inputblock
8430                         ]
8431                     }
8432                     
8433                 ];
8434         } else if ( this.fieldLabel.length) {
8435                 Roo.log(" label");
8436                  cfg.cn = [
8437                    
8438                     {
8439                         tag: 'label',
8440                         //cls : 'input-group-addon',
8441                         html : this.fieldLabel
8442                         
8443                     },
8444                     
8445                     inputblock
8446                     
8447                 ];
8448
8449         } else {
8450             
8451                    Roo.log(" no label && no align");
8452                 cfg.cn = [
8453                     
8454                         inputblock
8455                     
8456                 ];
8457                 
8458                 
8459         }
8460         
8461         if (this.disabled) {
8462             input.disabled=true;
8463         }
8464         
8465         return cfg;
8466         
8467     },
8468     /**
8469      * return the real textarea element.
8470      */
8471     inputEl: function ()
8472     {
8473         return this.el.select('textarea.form-control',true).first();
8474     }
8475 });
8476
8477  
8478 /*
8479  * - LGPL
8480  *
8481  * trigger field - base class for combo..
8482  * 
8483  */
8484  
8485 /**
8486  * @class Roo.bootstrap.TriggerField
8487  * @extends Roo.bootstrap.Input
8488  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8489  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8490  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8491  * for which you can provide a custom implementation.  For example:
8492  * <pre><code>
8493 var trigger = new Roo.bootstrap.TriggerField();
8494 trigger.onTriggerClick = myTriggerFn;
8495 trigger.applyTo('my-field');
8496 </code></pre>
8497  *
8498  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8499  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8500  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8501  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8502  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8503
8504  * @constructor
8505  * Create a new TriggerField.
8506  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8507  * to the base TextField)
8508  */
8509 Roo.bootstrap.TriggerField = function(config){
8510     this.mimicing = false;
8511     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8512 };
8513
8514 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8515     /**
8516      * @cfg {String} triggerClass A CSS class to apply to the trigger
8517      */
8518      /**
8519      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8520      */
8521     hideTrigger:false,
8522
8523     /**
8524      * @cfg {Boolean} removable (true|false) special filter default false
8525      */
8526     removable : false,
8527     
8528     /** @cfg {Boolean} grow @hide */
8529     /** @cfg {Number} growMin @hide */
8530     /** @cfg {Number} growMax @hide */
8531
8532     /**
8533      * @hide 
8534      * @method
8535      */
8536     autoSize: Roo.emptyFn,
8537     // private
8538     monitorTab : true,
8539     // private
8540     deferHeight : true,
8541
8542     
8543     actionMode : 'wrap',
8544     
8545     caret : false,
8546     
8547     
8548     getAutoCreate : function(){
8549        
8550         var align = this.labelAlign || this.parentLabelAlign();
8551         
8552         var id = Roo.id();
8553         
8554         var cfg = {
8555             cls: 'form-group' //input-group
8556         };
8557         
8558         
8559         var input =  {
8560             tag: 'input',
8561             id : id,
8562             type : this.inputType,
8563             cls : 'form-control',
8564             autocomplete: 'new-password',
8565             placeholder : this.placeholder || '' 
8566             
8567         };
8568         if (this.name) {
8569             input.name = this.name;
8570         }
8571         if (this.size) {
8572             input.cls += ' input-' + this.size;
8573         }
8574         
8575         if (this.disabled) {
8576             input.disabled=true;
8577         }
8578         
8579         var inputblock = input;
8580         
8581         if(this.hasFeedback && !this.allowBlank){
8582             
8583             var feedback = {
8584                 tag: 'span',
8585                 cls: 'glyphicon form-control-feedback'
8586             };
8587             
8588             if(this.removable && !this.editable && !this.tickable){
8589                 inputblock = {
8590                     cls : 'has-feedback',
8591                     cn :  [
8592                         inputblock,
8593                         {
8594                             tag: 'button',
8595                             html : 'x',
8596                             cls : 'roo-combo-removable-btn close'
8597                         },
8598                         feedback
8599                     ] 
8600                 };
8601             } else {
8602                 inputblock = {
8603                     cls : 'has-feedback',
8604                     cn :  [
8605                         inputblock,
8606                         feedback
8607                     ] 
8608                 };
8609             }
8610
8611         } else {
8612             if(this.removable && !this.editable && !this.tickable){
8613                 inputblock = {
8614                     cls : 'roo-removable',
8615                     cn :  [
8616                         inputblock,
8617                         {
8618                             tag: 'button',
8619                             html : 'x',
8620                             cls : 'roo-combo-removable-btn close'
8621                         }
8622                     ] 
8623                 };
8624             }
8625         }
8626         
8627         if (this.before || this.after) {
8628             
8629             inputblock = {
8630                 cls : 'input-group',
8631                 cn :  [] 
8632             };
8633             if (this.before) {
8634                 inputblock.cn.push({
8635                     tag :'span',
8636                     cls : 'input-group-addon',
8637                     html : this.before
8638                 });
8639             }
8640             
8641             inputblock.cn.push(input);
8642             
8643             if(this.hasFeedback && !this.allowBlank){
8644                 inputblock.cls += ' has-feedback';
8645                 inputblock.cn.push(feedback);
8646             }
8647             
8648             if (this.after) {
8649                 inputblock.cn.push({
8650                     tag :'span',
8651                     cls : 'input-group-addon',
8652                     html : this.after
8653                 });
8654             }
8655             
8656         };
8657         
8658         var box = {
8659             tag: 'div',
8660             cn: [
8661                 {
8662                     tag: 'input',
8663                     type : 'hidden',
8664                     cls: 'form-hidden-field'
8665                 },
8666                 inputblock
8667             ]
8668             
8669         };
8670         
8671         if(this.multiple){
8672             Roo.log('multiple');
8673             
8674             box = {
8675                 tag: 'div',
8676                 cn: [
8677                     {
8678                         tag: 'input',
8679                         type : 'hidden',
8680                         cls: 'form-hidden-field'
8681                     },
8682                     {
8683                         tag: 'ul',
8684                         cls: 'select2-choices',
8685                         cn:[
8686                             {
8687                                 tag: 'li',
8688                                 cls: 'select2-search-field',
8689                                 cn: [
8690
8691                                     inputblock
8692                                 ]
8693                             }
8694                         ]
8695                     }
8696                 ]
8697             }
8698         };
8699         
8700         var combobox = {
8701             cls: 'select2-container input-group',
8702             cn: [
8703                 box
8704 //                {
8705 //                    tag: 'ul',
8706 //                    cls: 'typeahead typeahead-long dropdown-menu',
8707 //                    style: 'display:none'
8708 //                }
8709             ]
8710         };
8711         
8712         if(!this.multiple && this.showToggleBtn){
8713             
8714             var caret = {
8715                         tag: 'span',
8716                         cls: 'caret'
8717              };
8718             if (this.caret != false) {
8719                 caret = {
8720                      tag: 'i',
8721                      cls: 'fa fa-' + this.caret
8722                 };
8723                 
8724             }
8725             
8726             combobox.cn.push({
8727                 tag :'span',
8728                 cls : 'input-group-addon btn dropdown-toggle',
8729                 cn : [
8730                     caret,
8731                     {
8732                         tag: 'span',
8733                         cls: 'combobox-clear',
8734                         cn  : [
8735                             {
8736                                 tag : 'i',
8737                                 cls: 'icon-remove'
8738                             }
8739                         ]
8740                     }
8741                 ]
8742
8743             })
8744         }
8745         
8746         if(this.multiple){
8747             combobox.cls += ' select2-container-multi';
8748         }
8749         
8750         if (align ==='left' && this.fieldLabel.length) {
8751             
8752                 Roo.log("left and has label");
8753                 cfg.cn = [
8754                     
8755                     {
8756                         tag: 'label',
8757                         'for' :  id,
8758                         cls : 'control-label col-sm-' + this.labelWidth,
8759                         html : this.fieldLabel
8760                         
8761                     },
8762                     {
8763                         cls : "col-sm-" + (12 - this.labelWidth), 
8764                         cn: [
8765                             combobox
8766                         ]
8767                     }
8768                     
8769                 ];
8770         } else if ( this.fieldLabel.length) {
8771                 Roo.log(" label");
8772                  cfg.cn = [
8773                    
8774                     {
8775                         tag: 'label',
8776                         //cls : 'input-group-addon',
8777                         html : this.fieldLabel
8778                         
8779                     },
8780                     
8781                     combobox
8782                     
8783                 ];
8784
8785         } else {
8786             
8787                 Roo.log(" no label && no align");
8788                 cfg = combobox
8789                      
8790                 
8791         }
8792          
8793         var settings=this;
8794         ['xs','sm','md','lg'].map(function(size){
8795             if (settings[size]) {
8796                 cfg.cls += ' col-' + size + '-' + settings[size];
8797             }
8798         });
8799         Roo.log(cfg);
8800         return cfg;
8801         
8802     },
8803     
8804     
8805     
8806     // private
8807     onResize : function(w, h){
8808 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8809 //        if(typeof w == 'number'){
8810 //            var x = w - this.trigger.getWidth();
8811 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8812 //            this.trigger.setStyle('left', x+'px');
8813 //        }
8814     },
8815
8816     // private
8817     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8818
8819     // private
8820     getResizeEl : function(){
8821         return this.inputEl();
8822     },
8823
8824     // private
8825     getPositionEl : function(){
8826         return this.inputEl();
8827     },
8828
8829     // private
8830     alignErrorIcon : function(){
8831         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8832     },
8833
8834     // private
8835     initEvents : function(){
8836         
8837         this.createList();
8838         
8839         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8840         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8841         if(!this.multiple && this.showToggleBtn){
8842             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8843             if(this.hideTrigger){
8844                 this.trigger.setDisplayed(false);
8845             }
8846             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8847         }
8848         
8849         if(this.multiple){
8850             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8851         }
8852         
8853         if(this.removable && !this.editable && !this.tickable){
8854             var close = this.closeTriggerEl();
8855             
8856             if(close){
8857                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8858                 close.on('click', this.removeBtnClick, this, close);
8859             }
8860         }
8861         
8862         //this.trigger.addClassOnOver('x-form-trigger-over');
8863         //this.trigger.addClassOnClick('x-form-trigger-click');
8864         
8865         //if(!this.width){
8866         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8867         //}
8868     },
8869     
8870     closeTriggerEl : function()
8871     {
8872         var close = this.el.select('.roo-combo-removable-btn', true).first();
8873         return close ? close : false;
8874     },
8875     
8876     removeBtnClick : function(e, h, el)
8877     {
8878         e.preventDefault();
8879         
8880         if(this.fireEvent("remove", this) !== false){
8881             this.reset();
8882         }
8883     },
8884     
8885     createList : function()
8886     {
8887         this.list = Roo.get(document.body).createChild({
8888             tag: 'ul',
8889             cls: 'typeahead typeahead-long dropdown-menu',
8890             style: 'display:none'
8891         });
8892         
8893         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8894         
8895     },
8896
8897     // private
8898     initTrigger : function(){
8899        
8900     },
8901
8902     // private
8903     onDestroy : function(){
8904         if(this.trigger){
8905             this.trigger.removeAllListeners();
8906           //  this.trigger.remove();
8907         }
8908         //if(this.wrap){
8909         //    this.wrap.remove();
8910         //}
8911         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8912     },
8913
8914     // private
8915     onFocus : function(){
8916         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8917         /*
8918         if(!this.mimicing){
8919             this.wrap.addClass('x-trigger-wrap-focus');
8920             this.mimicing = true;
8921             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8922             if(this.monitorTab){
8923                 this.el.on("keydown", this.checkTab, this);
8924             }
8925         }
8926         */
8927     },
8928
8929     // private
8930     checkTab : function(e){
8931         if(e.getKey() == e.TAB){
8932             this.triggerBlur();
8933         }
8934     },
8935
8936     // private
8937     onBlur : function(){
8938         // do nothing
8939     },
8940
8941     // private
8942     mimicBlur : function(e, t){
8943         /*
8944         if(!this.wrap.contains(t) && this.validateBlur()){
8945             this.triggerBlur();
8946         }
8947         */
8948     },
8949
8950     // private
8951     triggerBlur : function(){
8952         this.mimicing = false;
8953         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8954         if(this.monitorTab){
8955             this.el.un("keydown", this.checkTab, this);
8956         }
8957         //this.wrap.removeClass('x-trigger-wrap-focus');
8958         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8959     },
8960
8961     // private
8962     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8963     validateBlur : function(e, t){
8964         return true;
8965     },
8966
8967     // private
8968     onDisable : function(){
8969         this.inputEl().dom.disabled = true;
8970         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8971         //if(this.wrap){
8972         //    this.wrap.addClass('x-item-disabled');
8973         //}
8974     },
8975
8976     // private
8977     onEnable : function(){
8978         this.inputEl().dom.disabled = false;
8979         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8980         //if(this.wrap){
8981         //    this.el.removeClass('x-item-disabled');
8982         //}
8983     },
8984
8985     // private
8986     onShow : function(){
8987         var ae = this.getActionEl();
8988         
8989         if(ae){
8990             ae.dom.style.display = '';
8991             ae.dom.style.visibility = 'visible';
8992         }
8993     },
8994
8995     // private
8996     
8997     onHide : function(){
8998         var ae = this.getActionEl();
8999         ae.dom.style.display = 'none';
9000     },
9001
9002     /**
9003      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9004      * by an implementing function.
9005      * @method
9006      * @param {EventObject} e
9007      */
9008     onTriggerClick : Roo.emptyFn
9009 });
9010  /*
9011  * Based on:
9012  * Ext JS Library 1.1.1
9013  * Copyright(c) 2006-2007, Ext JS, LLC.
9014  *
9015  * Originally Released Under LGPL - original licence link has changed is not relivant.
9016  *
9017  * Fork - LGPL
9018  * <script type="text/javascript">
9019  */
9020
9021
9022 /**
9023  * @class Roo.data.SortTypes
9024  * @singleton
9025  * Defines the default sorting (casting?) comparison functions used when sorting data.
9026  */
9027 Roo.data.SortTypes = {
9028     /**
9029      * Default sort that does nothing
9030      * @param {Mixed} s The value being converted
9031      * @return {Mixed} The comparison value
9032      */
9033     none : function(s){
9034         return s;
9035     },
9036     
9037     /**
9038      * The regular expression used to strip tags
9039      * @type {RegExp}
9040      * @property
9041      */
9042     stripTagsRE : /<\/?[^>]+>/gi,
9043     
9044     /**
9045      * Strips all HTML tags to sort on text only
9046      * @param {Mixed} s The value being converted
9047      * @return {String} The comparison value
9048      */
9049     asText : function(s){
9050         return String(s).replace(this.stripTagsRE, "");
9051     },
9052     
9053     /**
9054      * Strips all HTML tags to sort on text only - Case insensitive
9055      * @param {Mixed} s The value being converted
9056      * @return {String} The comparison value
9057      */
9058     asUCText : function(s){
9059         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9060     },
9061     
9062     /**
9063      * Case insensitive string
9064      * @param {Mixed} s The value being converted
9065      * @return {String} The comparison value
9066      */
9067     asUCString : function(s) {
9068         return String(s).toUpperCase();
9069     },
9070     
9071     /**
9072      * Date sorting
9073      * @param {Mixed} s The value being converted
9074      * @return {Number} The comparison value
9075      */
9076     asDate : function(s) {
9077         if(!s){
9078             return 0;
9079         }
9080         if(s instanceof Date){
9081             return s.getTime();
9082         }
9083         return Date.parse(String(s));
9084     },
9085     
9086     /**
9087      * Float sorting
9088      * @param {Mixed} s The value being converted
9089      * @return {Float} The comparison value
9090      */
9091     asFloat : function(s) {
9092         var val = parseFloat(String(s).replace(/,/g, ""));
9093         if(isNaN(val)) val = 0;
9094         return val;
9095     },
9096     
9097     /**
9098      * Integer sorting
9099      * @param {Mixed} s The value being converted
9100      * @return {Number} The comparison value
9101      */
9102     asInt : function(s) {
9103         var val = parseInt(String(s).replace(/,/g, ""));
9104         if(isNaN(val)) val = 0;
9105         return val;
9106     }
9107 };/*
9108  * Based on:
9109  * Ext JS Library 1.1.1
9110  * Copyright(c) 2006-2007, Ext JS, LLC.
9111  *
9112  * Originally Released Under LGPL - original licence link has changed is not relivant.
9113  *
9114  * Fork - LGPL
9115  * <script type="text/javascript">
9116  */
9117
9118 /**
9119 * @class Roo.data.Record
9120  * Instances of this class encapsulate both record <em>definition</em> information, and record
9121  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9122  * to access Records cached in an {@link Roo.data.Store} object.<br>
9123  * <p>
9124  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9125  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9126  * objects.<br>
9127  * <p>
9128  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9129  * @constructor
9130  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9131  * {@link #create}. The parameters are the same.
9132  * @param {Array} data An associative Array of data values keyed by the field name.
9133  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9134  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9135  * not specified an integer id is generated.
9136  */
9137 Roo.data.Record = function(data, id){
9138     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9139     this.data = data;
9140 };
9141
9142 /**
9143  * Generate a constructor for a specific record layout.
9144  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9145  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9146  * Each field definition object may contain the following properties: <ul>
9147  * <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,
9148  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9149  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9150  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9151  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9152  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9153  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9154  * this may be omitted.</p></li>
9155  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9156  * <ul><li>auto (Default, implies no conversion)</li>
9157  * <li>string</li>
9158  * <li>int</li>
9159  * <li>float</li>
9160  * <li>boolean</li>
9161  * <li>date</li></ul></p></li>
9162  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9163  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9164  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9165  * by the Reader into an object that will be stored in the Record. It is passed the
9166  * following parameters:<ul>
9167  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9168  * </ul></p></li>
9169  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9170  * </ul>
9171  * <br>usage:<br><pre><code>
9172 var TopicRecord = Roo.data.Record.create(
9173     {name: 'title', mapping: 'topic_title'},
9174     {name: 'author', mapping: 'username'},
9175     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9176     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9177     {name: 'lastPoster', mapping: 'user2'},
9178     {name: 'excerpt', mapping: 'post_text'}
9179 );
9180
9181 var myNewRecord = new TopicRecord({
9182     title: 'Do my job please',
9183     author: 'noobie',
9184     totalPosts: 1,
9185     lastPost: new Date(),
9186     lastPoster: 'Animal',
9187     excerpt: 'No way dude!'
9188 });
9189 myStore.add(myNewRecord);
9190 </code></pre>
9191  * @method create
9192  * @static
9193  */
9194 Roo.data.Record.create = function(o){
9195     var f = function(){
9196         f.superclass.constructor.apply(this, arguments);
9197     };
9198     Roo.extend(f, Roo.data.Record);
9199     var p = f.prototype;
9200     p.fields = new Roo.util.MixedCollection(false, function(field){
9201         return field.name;
9202     });
9203     for(var i = 0, len = o.length; i < len; i++){
9204         p.fields.add(new Roo.data.Field(o[i]));
9205     }
9206     f.getField = function(name){
9207         return p.fields.get(name);  
9208     };
9209     return f;
9210 };
9211
9212 Roo.data.Record.AUTO_ID = 1000;
9213 Roo.data.Record.EDIT = 'edit';
9214 Roo.data.Record.REJECT = 'reject';
9215 Roo.data.Record.COMMIT = 'commit';
9216
9217 Roo.data.Record.prototype = {
9218     /**
9219      * Readonly flag - true if this record has been modified.
9220      * @type Boolean
9221      */
9222     dirty : false,
9223     editing : false,
9224     error: null,
9225     modified: null,
9226
9227     // private
9228     join : function(store){
9229         this.store = store;
9230     },
9231
9232     /**
9233      * Set the named field to the specified value.
9234      * @param {String} name The name of the field to set.
9235      * @param {Object} value The value to set the field to.
9236      */
9237     set : function(name, value){
9238         if(this.data[name] == value){
9239             return;
9240         }
9241         this.dirty = true;
9242         if(!this.modified){
9243             this.modified = {};
9244         }
9245         if(typeof this.modified[name] == 'undefined'){
9246             this.modified[name] = this.data[name];
9247         }
9248         this.data[name] = value;
9249         if(!this.editing && this.store){
9250             this.store.afterEdit(this);
9251         }       
9252     },
9253
9254     /**
9255      * Get the value of the named field.
9256      * @param {String} name The name of the field to get the value of.
9257      * @return {Object} The value of the field.
9258      */
9259     get : function(name){
9260         return this.data[name]; 
9261     },
9262
9263     // private
9264     beginEdit : function(){
9265         this.editing = true;
9266         this.modified = {}; 
9267     },
9268
9269     // private
9270     cancelEdit : function(){
9271         this.editing = false;
9272         delete this.modified;
9273     },
9274
9275     // private
9276     endEdit : function(){
9277         this.editing = false;
9278         if(this.dirty && this.store){
9279             this.store.afterEdit(this);
9280         }
9281     },
9282
9283     /**
9284      * Usually called by the {@link Roo.data.Store} which owns the Record.
9285      * Rejects all changes made to the Record since either creation, or the last commit operation.
9286      * Modified fields are reverted to their original values.
9287      * <p>
9288      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9289      * of reject operations.
9290      */
9291     reject : function(){
9292         var m = this.modified;
9293         for(var n in m){
9294             if(typeof m[n] != "function"){
9295                 this.data[n] = m[n];
9296             }
9297         }
9298         this.dirty = false;
9299         delete this.modified;
9300         this.editing = false;
9301         if(this.store){
9302             this.store.afterReject(this);
9303         }
9304     },
9305
9306     /**
9307      * Usually called by the {@link Roo.data.Store} which owns the Record.
9308      * Commits all changes made to the Record since either creation, or the last commit operation.
9309      * <p>
9310      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9311      * of commit operations.
9312      */
9313     commit : function(){
9314         this.dirty = false;
9315         delete this.modified;
9316         this.editing = false;
9317         if(this.store){
9318             this.store.afterCommit(this);
9319         }
9320     },
9321
9322     // private
9323     hasError : function(){
9324         return this.error != null;
9325     },
9326
9327     // private
9328     clearError : function(){
9329         this.error = null;
9330     },
9331
9332     /**
9333      * Creates a copy of this record.
9334      * @param {String} id (optional) A new record id if you don't want to use this record's id
9335      * @return {Record}
9336      */
9337     copy : function(newId) {
9338         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9339     }
9340 };/*
9341  * Based on:
9342  * Ext JS Library 1.1.1
9343  * Copyright(c) 2006-2007, Ext JS, LLC.
9344  *
9345  * Originally Released Under LGPL - original licence link has changed is not relivant.
9346  *
9347  * Fork - LGPL
9348  * <script type="text/javascript">
9349  */
9350
9351
9352
9353 /**
9354  * @class Roo.data.Store
9355  * @extends Roo.util.Observable
9356  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9357  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9358  * <p>
9359  * 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
9360  * has no knowledge of the format of the data returned by the Proxy.<br>
9361  * <p>
9362  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9363  * instances from the data object. These records are cached and made available through accessor functions.
9364  * @constructor
9365  * Creates a new Store.
9366  * @param {Object} config A config object containing the objects needed for the Store to access data,
9367  * and read the data into Records.
9368  */
9369 Roo.data.Store = function(config){
9370     this.data = new Roo.util.MixedCollection(false);
9371     this.data.getKey = function(o){
9372         return o.id;
9373     };
9374     this.baseParams = {};
9375     // private
9376     this.paramNames = {
9377         "start" : "start",
9378         "limit" : "limit",
9379         "sort" : "sort",
9380         "dir" : "dir",
9381         "multisort" : "_multisort"
9382     };
9383
9384     if(config && config.data){
9385         this.inlineData = config.data;
9386         delete config.data;
9387     }
9388
9389     Roo.apply(this, config);
9390     
9391     if(this.reader){ // reader passed
9392         this.reader = Roo.factory(this.reader, Roo.data);
9393         this.reader.xmodule = this.xmodule || false;
9394         if(!this.recordType){
9395             this.recordType = this.reader.recordType;
9396         }
9397         if(this.reader.onMetaChange){
9398             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9399         }
9400     }
9401
9402     if(this.recordType){
9403         this.fields = this.recordType.prototype.fields;
9404     }
9405     this.modified = [];
9406
9407     this.addEvents({
9408         /**
9409          * @event datachanged
9410          * Fires when the data cache has changed, and a widget which is using this Store
9411          * as a Record cache should refresh its view.
9412          * @param {Store} this
9413          */
9414         datachanged : true,
9415         /**
9416          * @event metachange
9417          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9418          * @param {Store} this
9419          * @param {Object} meta The JSON metadata
9420          */
9421         metachange : true,
9422         /**
9423          * @event add
9424          * Fires when Records have been added to the Store
9425          * @param {Store} this
9426          * @param {Roo.data.Record[]} records The array of Records added
9427          * @param {Number} index The index at which the record(s) were added
9428          */
9429         add : true,
9430         /**
9431          * @event remove
9432          * Fires when a Record has been removed from the Store
9433          * @param {Store} this
9434          * @param {Roo.data.Record} record The Record that was removed
9435          * @param {Number} index The index at which the record was removed
9436          */
9437         remove : true,
9438         /**
9439          * @event update
9440          * Fires when a Record has been updated
9441          * @param {Store} this
9442          * @param {Roo.data.Record} record The Record that was updated
9443          * @param {String} operation The update operation being performed.  Value may be one of:
9444          * <pre><code>
9445  Roo.data.Record.EDIT
9446  Roo.data.Record.REJECT
9447  Roo.data.Record.COMMIT
9448          * </code></pre>
9449          */
9450         update : true,
9451         /**
9452          * @event clear
9453          * Fires when the data cache has been cleared.
9454          * @param {Store} this
9455          */
9456         clear : true,
9457         /**
9458          * @event beforeload
9459          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9460          * the load action will be canceled.
9461          * @param {Store} this
9462          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9463          */
9464         beforeload : true,
9465         /**
9466          * @event beforeloadadd
9467          * Fires after a new set of Records has been loaded.
9468          * @param {Store} this
9469          * @param {Roo.data.Record[]} records The Records that were loaded
9470          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9471          */
9472         beforeloadadd : true,
9473         /**
9474          * @event load
9475          * Fires after a new set of Records has been loaded, before they are added to the store.
9476          * @param {Store} this
9477          * @param {Roo.data.Record[]} records The Records that were loaded
9478          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9479          * @params {Object} return from reader
9480          */
9481         load : true,
9482         /**
9483          * @event loadexception
9484          * Fires if an exception occurs in the Proxy during loading.
9485          * Called with the signature of the Proxy's "loadexception" event.
9486          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9487          * 
9488          * @param {Proxy} 
9489          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9490          * @param {Object} load options 
9491          * @param {Object} jsonData from your request (normally this contains the Exception)
9492          */
9493         loadexception : true
9494     });
9495     
9496     if(this.proxy){
9497         this.proxy = Roo.factory(this.proxy, Roo.data);
9498         this.proxy.xmodule = this.xmodule || false;
9499         this.relayEvents(this.proxy,  ["loadexception"]);
9500     }
9501     this.sortToggle = {};
9502     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9503
9504     Roo.data.Store.superclass.constructor.call(this);
9505
9506     if(this.inlineData){
9507         this.loadData(this.inlineData);
9508         delete this.inlineData;
9509     }
9510 };
9511
9512 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9513      /**
9514     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9515     * without a remote query - used by combo/forms at present.
9516     */
9517     
9518     /**
9519     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9520     */
9521     /**
9522     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9523     */
9524     /**
9525     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9526     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9527     */
9528     /**
9529     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9530     * on any HTTP request
9531     */
9532     /**
9533     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9534     */
9535     /**
9536     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9537     */
9538     multiSort: false,
9539     /**
9540     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9541     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9542     */
9543     remoteSort : false,
9544
9545     /**
9546     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9547      * loaded or when a record is removed. (defaults to false).
9548     */
9549     pruneModifiedRecords : false,
9550
9551     // private
9552     lastOptions : null,
9553
9554     /**
9555      * Add Records to the Store and fires the add event.
9556      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9557      */
9558     add : function(records){
9559         records = [].concat(records);
9560         for(var i = 0, len = records.length; i < len; i++){
9561             records[i].join(this);
9562         }
9563         var index = this.data.length;
9564         this.data.addAll(records);
9565         this.fireEvent("add", this, records, index);
9566     },
9567
9568     /**
9569      * Remove a Record from the Store and fires the remove event.
9570      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9571      */
9572     remove : function(record){
9573         var index = this.data.indexOf(record);
9574         this.data.removeAt(index);
9575         if(this.pruneModifiedRecords){
9576             this.modified.remove(record);
9577         }
9578         this.fireEvent("remove", this, record, index);
9579     },
9580
9581     /**
9582      * Remove all Records from the Store and fires the clear event.
9583      */
9584     removeAll : function(){
9585         this.data.clear();
9586         if(this.pruneModifiedRecords){
9587             this.modified = [];
9588         }
9589         this.fireEvent("clear", this);
9590     },
9591
9592     /**
9593      * Inserts Records to the Store at the given index and fires the add event.
9594      * @param {Number} index The start index at which to insert the passed Records.
9595      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9596      */
9597     insert : function(index, records){
9598         records = [].concat(records);
9599         for(var i = 0, len = records.length; i < len; i++){
9600             this.data.insert(index, records[i]);
9601             records[i].join(this);
9602         }
9603         this.fireEvent("add", this, records, index);
9604     },
9605
9606     /**
9607      * Get the index within the cache of the passed Record.
9608      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9609      * @return {Number} The index of the passed Record. Returns -1 if not found.
9610      */
9611     indexOf : function(record){
9612         return this.data.indexOf(record);
9613     },
9614
9615     /**
9616      * Get the index within the cache of the Record with the passed id.
9617      * @param {String} id The id of the Record to find.
9618      * @return {Number} The index of the Record. Returns -1 if not found.
9619      */
9620     indexOfId : function(id){
9621         return this.data.indexOfKey(id);
9622     },
9623
9624     /**
9625      * Get the Record with the specified id.
9626      * @param {String} id The id of the Record to find.
9627      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9628      */
9629     getById : function(id){
9630         return this.data.key(id);
9631     },
9632
9633     /**
9634      * Get the Record at the specified index.
9635      * @param {Number} index The index of the Record to find.
9636      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9637      */
9638     getAt : function(index){
9639         return this.data.itemAt(index);
9640     },
9641
9642     /**
9643      * Returns a range of Records between specified indices.
9644      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9645      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9646      * @return {Roo.data.Record[]} An array of Records
9647      */
9648     getRange : function(start, end){
9649         return this.data.getRange(start, end);
9650     },
9651
9652     // private
9653     storeOptions : function(o){
9654         o = Roo.apply({}, o);
9655         delete o.callback;
9656         delete o.scope;
9657         this.lastOptions = o;
9658     },
9659
9660     /**
9661      * Loads the Record cache from the configured Proxy using the configured Reader.
9662      * <p>
9663      * If using remote paging, then the first load call must specify the <em>start</em>
9664      * and <em>limit</em> properties in the options.params property to establish the initial
9665      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9666      * <p>
9667      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9668      * and this call will return before the new data has been loaded. Perform any post-processing
9669      * in a callback function, or in a "load" event handler.</strong>
9670      * <p>
9671      * @param {Object} options An object containing properties which control loading options:<ul>
9672      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9673      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9674      * passed the following arguments:<ul>
9675      * <li>r : Roo.data.Record[]</li>
9676      * <li>options: Options object from the load call</li>
9677      * <li>success: Boolean success indicator</li></ul></li>
9678      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9679      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9680      * </ul>
9681      */
9682     load : function(options){
9683         options = options || {};
9684         if(this.fireEvent("beforeload", this, options) !== false){
9685             this.storeOptions(options);
9686             var p = Roo.apply(options.params || {}, this.baseParams);
9687             // if meta was not loaded from remote source.. try requesting it.
9688             if (!this.reader.metaFromRemote) {
9689                 p._requestMeta = 1;
9690             }
9691             if(this.sortInfo && this.remoteSort){
9692                 var pn = this.paramNames;
9693                 p[pn["sort"]] = this.sortInfo.field;
9694                 p[pn["dir"]] = this.sortInfo.direction;
9695             }
9696             if (this.multiSort) {
9697                 var pn = this.paramNames;
9698                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9699             }
9700             
9701             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9702         }
9703     },
9704
9705     /**
9706      * Reloads the Record cache from the configured Proxy using the configured Reader and
9707      * the options from the last load operation performed.
9708      * @param {Object} options (optional) An object containing properties which may override the options
9709      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9710      * the most recently used options are reused).
9711      */
9712     reload : function(options){
9713         this.load(Roo.applyIf(options||{}, this.lastOptions));
9714     },
9715
9716     // private
9717     // Called as a callback by the Reader during a load operation.
9718     loadRecords : function(o, options, success){
9719         if(!o || success === false){
9720             if(success !== false){
9721                 this.fireEvent("load", this, [], options, o);
9722             }
9723             if(options.callback){
9724                 options.callback.call(options.scope || this, [], options, false);
9725             }
9726             return;
9727         }
9728         // if data returned failure - throw an exception.
9729         if (o.success === false) {
9730             // show a message if no listener is registered.
9731             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9732                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9733             }
9734             // loadmask wil be hooked into this..
9735             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9736             return;
9737         }
9738         var r = o.records, t = o.totalRecords || r.length;
9739         
9740         this.fireEvent("beforeloadadd", this, r, options, o);
9741         
9742         if(!options || options.add !== true){
9743             if(this.pruneModifiedRecords){
9744                 this.modified = [];
9745             }
9746             for(var i = 0, len = r.length; i < len; i++){
9747                 r[i].join(this);
9748             }
9749             if(this.snapshot){
9750                 this.data = this.snapshot;
9751                 delete this.snapshot;
9752             }
9753             this.data.clear();
9754             this.data.addAll(r);
9755             this.totalLength = t;
9756             this.applySort();
9757             this.fireEvent("datachanged", this);
9758         }else{
9759             this.totalLength = Math.max(t, this.data.length+r.length);
9760             this.add(r);
9761         }
9762         this.fireEvent("load", this, r, options, o);
9763         if(options.callback){
9764             options.callback.call(options.scope || this, r, options, true);
9765         }
9766     },
9767
9768
9769     /**
9770      * Loads data from a passed data block. A Reader which understands the format of the data
9771      * must have been configured in the constructor.
9772      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9773      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9774      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9775      */
9776     loadData : function(o, append){
9777         var r = this.reader.readRecords(o);
9778         this.loadRecords(r, {add: append}, true);
9779     },
9780
9781     /**
9782      * Gets the number of cached records.
9783      * <p>
9784      * <em>If using paging, this may not be the total size of the dataset. If the data object
9785      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9786      * the data set size</em>
9787      */
9788     getCount : function(){
9789         return this.data.length || 0;
9790     },
9791
9792     /**
9793      * Gets the total number of records in the dataset as returned by the server.
9794      * <p>
9795      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9796      * the dataset size</em>
9797      */
9798     getTotalCount : function(){
9799         return this.totalLength || 0;
9800     },
9801
9802     /**
9803      * Returns the sort state of the Store as an object with two properties:
9804      * <pre><code>
9805  field {String} The name of the field by which the Records are sorted
9806  direction {String} The sort order, "ASC" or "DESC"
9807      * </code></pre>
9808      */
9809     getSortState : function(){
9810         return this.sortInfo;
9811     },
9812
9813     // private
9814     applySort : function(){
9815         if(this.sortInfo && !this.remoteSort){
9816             var s = this.sortInfo, f = s.field;
9817             var st = this.fields.get(f).sortType;
9818             var fn = function(r1, r2){
9819                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9820                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9821             };
9822             this.data.sort(s.direction, fn);
9823             if(this.snapshot && this.snapshot != this.data){
9824                 this.snapshot.sort(s.direction, fn);
9825             }
9826         }
9827     },
9828
9829     /**
9830      * Sets the default sort column and order to be used by the next load operation.
9831      * @param {String} fieldName The name of the field to sort by.
9832      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9833      */
9834     setDefaultSort : function(field, dir){
9835         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9836     },
9837
9838     /**
9839      * Sort the Records.
9840      * If remote sorting is used, the sort is performed on the server, and the cache is
9841      * reloaded. If local sorting is used, the cache is sorted internally.
9842      * @param {String} fieldName The name of the field to sort by.
9843      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9844      */
9845     sort : function(fieldName, dir){
9846         var f = this.fields.get(fieldName);
9847         if(!dir){
9848             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9849             
9850             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9851                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9852             }else{
9853                 dir = f.sortDir;
9854             }
9855         }
9856         this.sortToggle[f.name] = dir;
9857         this.sortInfo = {field: f.name, direction: dir};
9858         if(!this.remoteSort){
9859             this.applySort();
9860             this.fireEvent("datachanged", this);
9861         }else{
9862             this.load(this.lastOptions);
9863         }
9864     },
9865
9866     /**
9867      * Calls the specified function for each of the Records in the cache.
9868      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9869      * Returning <em>false</em> aborts and exits the iteration.
9870      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9871      */
9872     each : function(fn, scope){
9873         this.data.each(fn, scope);
9874     },
9875
9876     /**
9877      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9878      * (e.g., during paging).
9879      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9880      */
9881     getModifiedRecords : function(){
9882         return this.modified;
9883     },
9884
9885     // private
9886     createFilterFn : function(property, value, anyMatch){
9887         if(!value.exec){ // not a regex
9888             value = String(value);
9889             if(value.length == 0){
9890                 return false;
9891             }
9892             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9893         }
9894         return function(r){
9895             return value.test(r.data[property]);
9896         };
9897     },
9898
9899     /**
9900      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9901      * @param {String} property A field on your records
9902      * @param {Number} start The record index to start at (defaults to 0)
9903      * @param {Number} end The last record index to include (defaults to length - 1)
9904      * @return {Number} The sum
9905      */
9906     sum : function(property, start, end){
9907         var rs = this.data.items, v = 0;
9908         start = start || 0;
9909         end = (end || end === 0) ? end : rs.length-1;
9910
9911         for(var i = start; i <= end; i++){
9912             v += (rs[i].data[property] || 0);
9913         }
9914         return v;
9915     },
9916
9917     /**
9918      * Filter the records by a specified property.
9919      * @param {String} field A field on your records
9920      * @param {String/RegExp} value Either a string that the field
9921      * should start with or a RegExp to test against the field
9922      * @param {Boolean} anyMatch True to match any part not just the beginning
9923      */
9924     filter : function(property, value, anyMatch){
9925         var fn = this.createFilterFn(property, value, anyMatch);
9926         return fn ? this.filterBy(fn) : this.clearFilter();
9927     },
9928
9929     /**
9930      * Filter by a function. The specified function will be called with each
9931      * record in this data source. If the function returns true the record is included,
9932      * otherwise it is filtered.
9933      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9934      * @param {Object} scope (optional) The scope of the function (defaults to this)
9935      */
9936     filterBy : function(fn, scope){
9937         this.snapshot = this.snapshot || this.data;
9938         this.data = this.queryBy(fn, scope||this);
9939         this.fireEvent("datachanged", this);
9940     },
9941
9942     /**
9943      * Query the records by a specified property.
9944      * @param {String} field A field on your records
9945      * @param {String/RegExp} value Either a string that the field
9946      * should start with or a RegExp to test against the field
9947      * @param {Boolean} anyMatch True to match any part not just the beginning
9948      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9949      */
9950     query : function(property, value, anyMatch){
9951         var fn = this.createFilterFn(property, value, anyMatch);
9952         return fn ? this.queryBy(fn) : this.data.clone();
9953     },
9954
9955     /**
9956      * Query by a function. The specified function will be called with each
9957      * record in this data source. If the function returns true the record is included
9958      * in the results.
9959      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9960      * @param {Object} scope (optional) The scope of the function (defaults to this)
9961       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9962      **/
9963     queryBy : function(fn, scope){
9964         var data = this.snapshot || this.data;
9965         return data.filterBy(fn, scope||this);
9966     },
9967
9968     /**
9969      * Collects unique values for a particular dataIndex from this store.
9970      * @param {String} dataIndex The property to collect
9971      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9972      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9973      * @return {Array} An array of the unique values
9974      **/
9975     collect : function(dataIndex, allowNull, bypassFilter){
9976         var d = (bypassFilter === true && this.snapshot) ?
9977                 this.snapshot.items : this.data.items;
9978         var v, sv, r = [], l = {};
9979         for(var i = 0, len = d.length; i < len; i++){
9980             v = d[i].data[dataIndex];
9981             sv = String(v);
9982             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9983                 l[sv] = true;
9984                 r[r.length] = v;
9985             }
9986         }
9987         return r;
9988     },
9989
9990     /**
9991      * Revert to a view of the Record cache with no filtering applied.
9992      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9993      */
9994     clearFilter : function(suppressEvent){
9995         if(this.snapshot && this.snapshot != this.data){
9996             this.data = this.snapshot;
9997             delete this.snapshot;
9998             if(suppressEvent !== true){
9999                 this.fireEvent("datachanged", this);
10000             }
10001         }
10002     },
10003
10004     // private
10005     afterEdit : function(record){
10006         if(this.modified.indexOf(record) == -1){
10007             this.modified.push(record);
10008         }
10009         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10010     },
10011     
10012     // private
10013     afterReject : function(record){
10014         this.modified.remove(record);
10015         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10016     },
10017
10018     // private
10019     afterCommit : function(record){
10020         this.modified.remove(record);
10021         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10022     },
10023
10024     /**
10025      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10026      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10027      */
10028     commitChanges : function(){
10029         var m = this.modified.slice(0);
10030         this.modified = [];
10031         for(var i = 0, len = m.length; i < len; i++){
10032             m[i].commit();
10033         }
10034     },
10035
10036     /**
10037      * Cancel outstanding changes on all changed records.
10038      */
10039     rejectChanges : function(){
10040         var m = this.modified.slice(0);
10041         this.modified = [];
10042         for(var i = 0, len = m.length; i < len; i++){
10043             m[i].reject();
10044         }
10045     },
10046
10047     onMetaChange : function(meta, rtype, o){
10048         this.recordType = rtype;
10049         this.fields = rtype.prototype.fields;
10050         delete this.snapshot;
10051         this.sortInfo = meta.sortInfo || this.sortInfo;
10052         this.modified = [];
10053         this.fireEvent('metachange', this, this.reader.meta);
10054     },
10055     
10056     moveIndex : function(data, type)
10057     {
10058         var index = this.indexOf(data);
10059         
10060         var newIndex = index + type;
10061         
10062         this.remove(data);
10063         
10064         this.insert(newIndex, data);
10065         
10066     }
10067 });/*
10068  * Based on:
10069  * Ext JS Library 1.1.1
10070  * Copyright(c) 2006-2007, Ext JS, LLC.
10071  *
10072  * Originally Released Under LGPL - original licence link has changed is not relivant.
10073  *
10074  * Fork - LGPL
10075  * <script type="text/javascript">
10076  */
10077
10078 /**
10079  * @class Roo.data.SimpleStore
10080  * @extends Roo.data.Store
10081  * Small helper class to make creating Stores from Array data easier.
10082  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10083  * @cfg {Array} fields An array of field definition objects, or field name strings.
10084  * @cfg {Array} data The multi-dimensional array of data
10085  * @constructor
10086  * @param {Object} config
10087  */
10088 Roo.data.SimpleStore = function(config){
10089     Roo.data.SimpleStore.superclass.constructor.call(this, {
10090         isLocal : true,
10091         reader: new Roo.data.ArrayReader({
10092                 id: config.id
10093             },
10094             Roo.data.Record.create(config.fields)
10095         ),
10096         proxy : new Roo.data.MemoryProxy(config.data)
10097     });
10098     this.load();
10099 };
10100 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10101  * Based on:
10102  * Ext JS Library 1.1.1
10103  * Copyright(c) 2006-2007, Ext JS, LLC.
10104  *
10105  * Originally Released Under LGPL - original licence link has changed is not relivant.
10106  *
10107  * Fork - LGPL
10108  * <script type="text/javascript">
10109  */
10110
10111 /**
10112 /**
10113  * @extends Roo.data.Store
10114  * @class Roo.data.JsonStore
10115  * Small helper class to make creating Stores for JSON data easier. <br/>
10116 <pre><code>
10117 var store = new Roo.data.JsonStore({
10118     url: 'get-images.php',
10119     root: 'images',
10120     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10121 });
10122 </code></pre>
10123  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10124  * JsonReader and HttpProxy (unless inline data is provided).</b>
10125  * @cfg {Array} fields An array of field definition objects, or field name strings.
10126  * @constructor
10127  * @param {Object} config
10128  */
10129 Roo.data.JsonStore = function(c){
10130     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10131         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10132         reader: new Roo.data.JsonReader(c, c.fields)
10133     }));
10134 };
10135 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10136  * Based on:
10137  * Ext JS Library 1.1.1
10138  * Copyright(c) 2006-2007, Ext JS, LLC.
10139  *
10140  * Originally Released Under LGPL - original licence link has changed is not relivant.
10141  *
10142  * Fork - LGPL
10143  * <script type="text/javascript">
10144  */
10145
10146  
10147 Roo.data.Field = function(config){
10148     if(typeof config == "string"){
10149         config = {name: config};
10150     }
10151     Roo.apply(this, config);
10152     
10153     if(!this.type){
10154         this.type = "auto";
10155     }
10156     
10157     var st = Roo.data.SortTypes;
10158     // named sortTypes are supported, here we look them up
10159     if(typeof this.sortType == "string"){
10160         this.sortType = st[this.sortType];
10161     }
10162     
10163     // set default sortType for strings and dates
10164     if(!this.sortType){
10165         switch(this.type){
10166             case "string":
10167                 this.sortType = st.asUCString;
10168                 break;
10169             case "date":
10170                 this.sortType = st.asDate;
10171                 break;
10172             default:
10173                 this.sortType = st.none;
10174         }
10175     }
10176
10177     // define once
10178     var stripRe = /[\$,%]/g;
10179
10180     // prebuilt conversion function for this field, instead of
10181     // switching every time we're reading a value
10182     if(!this.convert){
10183         var cv, dateFormat = this.dateFormat;
10184         switch(this.type){
10185             case "":
10186             case "auto":
10187             case undefined:
10188                 cv = function(v){ return v; };
10189                 break;
10190             case "string":
10191                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10192                 break;
10193             case "int":
10194                 cv = function(v){
10195                     return v !== undefined && v !== null && v !== '' ?
10196                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10197                     };
10198                 break;
10199             case "float":
10200                 cv = function(v){
10201                     return v !== undefined && v !== null && v !== '' ?
10202                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10203                     };
10204                 break;
10205             case "bool":
10206             case "boolean":
10207                 cv = function(v){ return v === true || v === "true" || v == 1; };
10208                 break;
10209             case "date":
10210                 cv = function(v){
10211                     if(!v){
10212                         return '';
10213                     }
10214                     if(v instanceof Date){
10215                         return v;
10216                     }
10217                     if(dateFormat){
10218                         if(dateFormat == "timestamp"){
10219                             return new Date(v*1000);
10220                         }
10221                         return Date.parseDate(v, dateFormat);
10222                     }
10223                     var parsed = Date.parse(v);
10224                     return parsed ? new Date(parsed) : null;
10225                 };
10226              break;
10227             
10228         }
10229         this.convert = cv;
10230     }
10231 };
10232
10233 Roo.data.Field.prototype = {
10234     dateFormat: null,
10235     defaultValue: "",
10236     mapping: null,
10237     sortType : null,
10238     sortDir : "ASC"
10239 };/*
10240  * Based on:
10241  * Ext JS Library 1.1.1
10242  * Copyright(c) 2006-2007, Ext JS, LLC.
10243  *
10244  * Originally Released Under LGPL - original licence link has changed is not relivant.
10245  *
10246  * Fork - LGPL
10247  * <script type="text/javascript">
10248  */
10249  
10250 // Base class for reading structured data from a data source.  This class is intended to be
10251 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10252
10253 /**
10254  * @class Roo.data.DataReader
10255  * Base class for reading structured data from a data source.  This class is intended to be
10256  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10257  */
10258
10259 Roo.data.DataReader = function(meta, recordType){
10260     
10261     this.meta = meta;
10262     
10263     this.recordType = recordType instanceof Array ? 
10264         Roo.data.Record.create(recordType) : recordType;
10265 };
10266
10267 Roo.data.DataReader.prototype = {
10268      /**
10269      * Create an empty record
10270      * @param {Object} data (optional) - overlay some values
10271      * @return {Roo.data.Record} record created.
10272      */
10273     newRow :  function(d) {
10274         var da =  {};
10275         this.recordType.prototype.fields.each(function(c) {
10276             switch( c.type) {
10277                 case 'int' : da[c.name] = 0; break;
10278                 case 'date' : da[c.name] = new Date(); break;
10279                 case 'float' : da[c.name] = 0.0; break;
10280                 case 'boolean' : da[c.name] = false; break;
10281                 default : da[c.name] = ""; break;
10282             }
10283             
10284         });
10285         return new this.recordType(Roo.apply(da, d));
10286     }
10287     
10288 };/*
10289  * Based on:
10290  * Ext JS Library 1.1.1
10291  * Copyright(c) 2006-2007, Ext JS, LLC.
10292  *
10293  * Originally Released Under LGPL - original licence link has changed is not relivant.
10294  *
10295  * Fork - LGPL
10296  * <script type="text/javascript">
10297  */
10298
10299 /**
10300  * @class Roo.data.DataProxy
10301  * @extends Roo.data.Observable
10302  * This class is an abstract base class for implementations which provide retrieval of
10303  * unformatted data objects.<br>
10304  * <p>
10305  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10306  * (of the appropriate type which knows how to parse the data object) to provide a block of
10307  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10308  * <p>
10309  * Custom implementations must implement the load method as described in
10310  * {@link Roo.data.HttpProxy#load}.
10311  */
10312 Roo.data.DataProxy = function(){
10313     this.addEvents({
10314         /**
10315          * @event beforeload
10316          * Fires before a network request is made to retrieve a data object.
10317          * @param {Object} This DataProxy object.
10318          * @param {Object} params The params parameter to the load function.
10319          */
10320         beforeload : true,
10321         /**
10322          * @event load
10323          * Fires before the load method's callback is called.
10324          * @param {Object} This DataProxy object.
10325          * @param {Object} o The data object.
10326          * @param {Object} arg The callback argument object passed to the load function.
10327          */
10328         load : true,
10329         /**
10330          * @event loadexception
10331          * Fires if an Exception occurs during data retrieval.
10332          * @param {Object} This DataProxy object.
10333          * @param {Object} o The data object.
10334          * @param {Object} arg The callback argument object passed to the load function.
10335          * @param {Object} e The Exception.
10336          */
10337         loadexception : true
10338     });
10339     Roo.data.DataProxy.superclass.constructor.call(this);
10340 };
10341
10342 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10343
10344     /**
10345      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10346      */
10347 /*
10348  * Based on:
10349  * Ext JS Library 1.1.1
10350  * Copyright(c) 2006-2007, Ext JS, LLC.
10351  *
10352  * Originally Released Under LGPL - original licence link has changed is not relivant.
10353  *
10354  * Fork - LGPL
10355  * <script type="text/javascript">
10356  */
10357 /**
10358  * @class Roo.data.MemoryProxy
10359  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10360  * to the Reader when its load method is called.
10361  * @constructor
10362  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10363  */
10364 Roo.data.MemoryProxy = function(data){
10365     if (data.data) {
10366         data = data.data;
10367     }
10368     Roo.data.MemoryProxy.superclass.constructor.call(this);
10369     this.data = data;
10370 };
10371
10372 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10373     /**
10374      * Load data from the requested source (in this case an in-memory
10375      * data object passed to the constructor), read the data object into
10376      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10377      * process that block using the passed callback.
10378      * @param {Object} params This parameter is not used by the MemoryProxy class.
10379      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10380      * object into a block of Roo.data.Records.
10381      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10382      * The function must be passed <ul>
10383      * <li>The Record block object</li>
10384      * <li>The "arg" argument from the load function</li>
10385      * <li>A boolean success indicator</li>
10386      * </ul>
10387      * @param {Object} scope The scope in which to call the callback
10388      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10389      */
10390     load : function(params, reader, callback, scope, arg){
10391         params = params || {};
10392         var result;
10393         try {
10394             result = reader.readRecords(this.data);
10395         }catch(e){
10396             this.fireEvent("loadexception", this, arg, null, e);
10397             callback.call(scope, null, arg, false);
10398             return;
10399         }
10400         callback.call(scope, result, arg, true);
10401     },
10402     
10403     // private
10404     update : function(params, records){
10405         
10406     }
10407 });/*
10408  * Based on:
10409  * Ext JS Library 1.1.1
10410  * Copyright(c) 2006-2007, Ext JS, LLC.
10411  *
10412  * Originally Released Under LGPL - original licence link has changed is not relivant.
10413  *
10414  * Fork - LGPL
10415  * <script type="text/javascript">
10416  */
10417 /**
10418  * @class Roo.data.HttpProxy
10419  * @extends Roo.data.DataProxy
10420  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10421  * configured to reference a certain URL.<br><br>
10422  * <p>
10423  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10424  * from which the running page was served.<br><br>
10425  * <p>
10426  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10427  * <p>
10428  * Be aware that to enable the browser to parse an XML document, the server must set
10429  * the Content-Type header in the HTTP response to "text/xml".
10430  * @constructor
10431  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10432  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10433  * will be used to make the request.
10434  */
10435 Roo.data.HttpProxy = function(conn){
10436     Roo.data.HttpProxy.superclass.constructor.call(this);
10437     // is conn a conn config or a real conn?
10438     this.conn = conn;
10439     this.useAjax = !conn || !conn.events;
10440   
10441 };
10442
10443 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10444     // thse are take from connection...
10445     
10446     /**
10447      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10448      */
10449     /**
10450      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10451      * extra parameters to each request made by this object. (defaults to undefined)
10452      */
10453     /**
10454      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10455      *  to each request made by this object. (defaults to undefined)
10456      */
10457     /**
10458      * @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)
10459      */
10460     /**
10461      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10462      */
10463      /**
10464      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10465      * @type Boolean
10466      */
10467   
10468
10469     /**
10470      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10471      * @type Boolean
10472      */
10473     /**
10474      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10475      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10476      * a finer-grained basis than the DataProxy events.
10477      */
10478     getConnection : function(){
10479         return this.useAjax ? Roo.Ajax : this.conn;
10480     },
10481
10482     /**
10483      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10484      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10485      * process that block using the passed callback.
10486      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10487      * for the request to the remote server.
10488      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10489      * object into a block of Roo.data.Records.
10490      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10491      * The function must be passed <ul>
10492      * <li>The Record block object</li>
10493      * <li>The "arg" argument from the load function</li>
10494      * <li>A boolean success indicator</li>
10495      * </ul>
10496      * @param {Object} scope The scope in which to call the callback
10497      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10498      */
10499     load : function(params, reader, callback, scope, arg){
10500         if(this.fireEvent("beforeload", this, params) !== false){
10501             var  o = {
10502                 params : params || {},
10503                 request: {
10504                     callback : callback,
10505                     scope : scope,
10506                     arg : arg
10507                 },
10508                 reader: reader,
10509                 callback : this.loadResponse,
10510                 scope: this
10511             };
10512             if(this.useAjax){
10513                 Roo.applyIf(o, this.conn);
10514                 if(this.activeRequest){
10515                     Roo.Ajax.abort(this.activeRequest);
10516                 }
10517                 this.activeRequest = Roo.Ajax.request(o);
10518             }else{
10519                 this.conn.request(o);
10520             }
10521         }else{
10522             callback.call(scope||this, null, arg, false);
10523         }
10524     },
10525
10526     // private
10527     loadResponse : function(o, success, response){
10528         delete this.activeRequest;
10529         if(!success){
10530             this.fireEvent("loadexception", this, o, response);
10531             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10532             return;
10533         }
10534         var result;
10535         try {
10536             result = o.reader.read(response);
10537         }catch(e){
10538             this.fireEvent("loadexception", this, o, response, e);
10539             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10540             return;
10541         }
10542         
10543         this.fireEvent("load", this, o, o.request.arg);
10544         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10545     },
10546
10547     // private
10548     update : function(dataSet){
10549
10550     },
10551
10552     // private
10553     updateResponse : function(dataSet){
10554
10555     }
10556 });/*
10557  * Based on:
10558  * Ext JS Library 1.1.1
10559  * Copyright(c) 2006-2007, Ext JS, LLC.
10560  *
10561  * Originally Released Under LGPL - original licence link has changed is not relivant.
10562  *
10563  * Fork - LGPL
10564  * <script type="text/javascript">
10565  */
10566
10567 /**
10568  * @class Roo.data.ScriptTagProxy
10569  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10570  * other than the originating domain of the running page.<br><br>
10571  * <p>
10572  * <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
10573  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10574  * <p>
10575  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10576  * source code that is used as the source inside a &lt;script> tag.<br><br>
10577  * <p>
10578  * In order for the browser to process the returned data, the server must wrap the data object
10579  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10580  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10581  * depending on whether the callback name was passed:
10582  * <p>
10583  * <pre><code>
10584 boolean scriptTag = false;
10585 String cb = request.getParameter("callback");
10586 if (cb != null) {
10587     scriptTag = true;
10588     response.setContentType("text/javascript");
10589 } else {
10590     response.setContentType("application/x-json");
10591 }
10592 Writer out = response.getWriter();
10593 if (scriptTag) {
10594     out.write(cb + "(");
10595 }
10596 out.print(dataBlock.toJsonString());
10597 if (scriptTag) {
10598     out.write(");");
10599 }
10600 </pre></code>
10601  *
10602  * @constructor
10603  * @param {Object} config A configuration object.
10604  */
10605 Roo.data.ScriptTagProxy = function(config){
10606     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10607     Roo.apply(this, config);
10608     this.head = document.getElementsByTagName("head")[0];
10609 };
10610
10611 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10612
10613 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10614     /**
10615      * @cfg {String} url The URL from which to request the data object.
10616      */
10617     /**
10618      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10619      */
10620     timeout : 30000,
10621     /**
10622      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10623      * the server the name of the callback function set up by the load call to process the returned data object.
10624      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10625      * javascript output which calls this named function passing the data object as its only parameter.
10626      */
10627     callbackParam : "callback",
10628     /**
10629      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10630      * name to the request.
10631      */
10632     nocache : true,
10633
10634     /**
10635      * Load data from the configured URL, read the data object into
10636      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10637      * process that block using the passed callback.
10638      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10639      * for the request to the remote server.
10640      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10641      * object into a block of Roo.data.Records.
10642      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10643      * The function must be passed <ul>
10644      * <li>The Record block object</li>
10645      * <li>The "arg" argument from the load function</li>
10646      * <li>A boolean success indicator</li>
10647      * </ul>
10648      * @param {Object} scope The scope in which to call the callback
10649      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10650      */
10651     load : function(params, reader, callback, scope, arg){
10652         if(this.fireEvent("beforeload", this, params) !== false){
10653
10654             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10655
10656             var url = this.url;
10657             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10658             if(this.nocache){
10659                 url += "&_dc=" + (new Date().getTime());
10660             }
10661             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10662             var trans = {
10663                 id : transId,
10664                 cb : "stcCallback"+transId,
10665                 scriptId : "stcScript"+transId,
10666                 params : params,
10667                 arg : arg,
10668                 url : url,
10669                 callback : callback,
10670                 scope : scope,
10671                 reader : reader
10672             };
10673             var conn = this;
10674
10675             window[trans.cb] = function(o){
10676                 conn.handleResponse(o, trans);
10677             };
10678
10679             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10680
10681             if(this.autoAbort !== false){
10682                 this.abort();
10683             }
10684
10685             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10686
10687             var script = document.createElement("script");
10688             script.setAttribute("src", url);
10689             script.setAttribute("type", "text/javascript");
10690             script.setAttribute("id", trans.scriptId);
10691             this.head.appendChild(script);
10692
10693             this.trans = trans;
10694         }else{
10695             callback.call(scope||this, null, arg, false);
10696         }
10697     },
10698
10699     // private
10700     isLoading : function(){
10701         return this.trans ? true : false;
10702     },
10703
10704     /**
10705      * Abort the current server request.
10706      */
10707     abort : function(){
10708         if(this.isLoading()){
10709             this.destroyTrans(this.trans);
10710         }
10711     },
10712
10713     // private
10714     destroyTrans : function(trans, isLoaded){
10715         this.head.removeChild(document.getElementById(trans.scriptId));
10716         clearTimeout(trans.timeoutId);
10717         if(isLoaded){
10718             window[trans.cb] = undefined;
10719             try{
10720                 delete window[trans.cb];
10721             }catch(e){}
10722         }else{
10723             // if hasn't been loaded, wait for load to remove it to prevent script error
10724             window[trans.cb] = function(){
10725                 window[trans.cb] = undefined;
10726                 try{
10727                     delete window[trans.cb];
10728                 }catch(e){}
10729             };
10730         }
10731     },
10732
10733     // private
10734     handleResponse : function(o, trans){
10735         this.trans = false;
10736         this.destroyTrans(trans, true);
10737         var result;
10738         try {
10739             result = trans.reader.readRecords(o);
10740         }catch(e){
10741             this.fireEvent("loadexception", this, o, trans.arg, e);
10742             trans.callback.call(trans.scope||window, null, trans.arg, false);
10743             return;
10744         }
10745         this.fireEvent("load", this, o, trans.arg);
10746         trans.callback.call(trans.scope||window, result, trans.arg, true);
10747     },
10748
10749     // private
10750     handleFailure : function(trans){
10751         this.trans = false;
10752         this.destroyTrans(trans, false);
10753         this.fireEvent("loadexception", this, null, trans.arg);
10754         trans.callback.call(trans.scope||window, null, trans.arg, false);
10755     }
10756 });/*
10757  * Based on:
10758  * Ext JS Library 1.1.1
10759  * Copyright(c) 2006-2007, Ext JS, LLC.
10760  *
10761  * Originally Released Under LGPL - original licence link has changed is not relivant.
10762  *
10763  * Fork - LGPL
10764  * <script type="text/javascript">
10765  */
10766
10767 /**
10768  * @class Roo.data.JsonReader
10769  * @extends Roo.data.DataReader
10770  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10771  * based on mappings in a provided Roo.data.Record constructor.
10772  * 
10773  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10774  * in the reply previously. 
10775  * 
10776  * <p>
10777  * Example code:
10778  * <pre><code>
10779 var RecordDef = Roo.data.Record.create([
10780     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10781     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10782 ]);
10783 var myReader = new Roo.data.JsonReader({
10784     totalProperty: "results",    // The property which contains the total dataset size (optional)
10785     root: "rows",                // The property which contains an Array of row objects
10786     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10787 }, RecordDef);
10788 </code></pre>
10789  * <p>
10790  * This would consume a JSON file like this:
10791  * <pre><code>
10792 { 'results': 2, 'rows': [
10793     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10794     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10795 }
10796 </code></pre>
10797  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10798  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10799  * paged from the remote server.
10800  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10801  * @cfg {String} root name of the property which contains the Array of row objects.
10802  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10803  * @cfg {Array} fields Array of field definition objects
10804  * @constructor
10805  * Create a new JsonReader
10806  * @param {Object} meta Metadata configuration options
10807  * @param {Object} recordType Either an Array of field definition objects,
10808  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10809  */
10810 Roo.data.JsonReader = function(meta, recordType){
10811     
10812     meta = meta || {};
10813     // set some defaults:
10814     Roo.applyIf(meta, {
10815         totalProperty: 'total',
10816         successProperty : 'success',
10817         root : 'data',
10818         id : 'id'
10819     });
10820     
10821     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10822 };
10823 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10824     
10825     /**
10826      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10827      * Used by Store query builder to append _requestMeta to params.
10828      * 
10829      */
10830     metaFromRemote : false,
10831     /**
10832      * This method is only used by a DataProxy which has retrieved data from a remote server.
10833      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10834      * @return {Object} data A data block which is used by an Roo.data.Store object as
10835      * a cache of Roo.data.Records.
10836      */
10837     read : function(response){
10838         var json = response.responseText;
10839        
10840         var o = /* eval:var:o */ eval("("+json+")");
10841         if(!o) {
10842             throw {message: "JsonReader.read: Json object not found"};
10843         }
10844         
10845         if(o.metaData){
10846             
10847             delete this.ef;
10848             this.metaFromRemote = true;
10849             this.meta = o.metaData;
10850             this.recordType = Roo.data.Record.create(o.metaData.fields);
10851             this.onMetaChange(this.meta, this.recordType, o);
10852         }
10853         return this.readRecords(o);
10854     },
10855
10856     // private function a store will implement
10857     onMetaChange : function(meta, recordType, o){
10858
10859     },
10860
10861     /**
10862          * @ignore
10863          */
10864     simpleAccess: function(obj, subsc) {
10865         return obj[subsc];
10866     },
10867
10868         /**
10869          * @ignore
10870          */
10871     getJsonAccessor: function(){
10872         var re = /[\[\.]/;
10873         return function(expr) {
10874             try {
10875                 return(re.test(expr))
10876                     ? new Function("obj", "return obj." + expr)
10877                     : function(obj){
10878                         return obj[expr];
10879                     };
10880             } catch(e){}
10881             return Roo.emptyFn;
10882         };
10883     }(),
10884
10885     /**
10886      * Create a data block containing Roo.data.Records from an XML document.
10887      * @param {Object} o An object which contains an Array of row objects in the property specified
10888      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10889      * which contains the total size of the dataset.
10890      * @return {Object} data A data block which is used by an Roo.data.Store object as
10891      * a cache of Roo.data.Records.
10892      */
10893     readRecords : function(o){
10894         /**
10895          * After any data loads, the raw JSON data is available for further custom processing.
10896          * @type Object
10897          */
10898         this.o = o;
10899         var s = this.meta, Record = this.recordType,
10900             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10901
10902 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10903         if (!this.ef) {
10904             if(s.totalProperty) {
10905                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10906                 }
10907                 if(s.successProperty) {
10908                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10909                 }
10910                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10911                 if (s.id) {
10912                         var g = this.getJsonAccessor(s.id);
10913                         this.getId = function(rec) {
10914                                 var r = g(rec);  
10915                                 return (r === undefined || r === "") ? null : r;
10916                         };
10917                 } else {
10918                         this.getId = function(){return null;};
10919                 }
10920             this.ef = [];
10921             for(var jj = 0; jj < fl; jj++){
10922                 f = fi[jj];
10923                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10924                 this.ef[jj] = this.getJsonAccessor(map);
10925             }
10926         }
10927
10928         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10929         if(s.totalProperty){
10930             var vt = parseInt(this.getTotal(o), 10);
10931             if(!isNaN(vt)){
10932                 totalRecords = vt;
10933             }
10934         }
10935         if(s.successProperty){
10936             var vs = this.getSuccess(o);
10937             if(vs === false || vs === 'false'){
10938                 success = false;
10939             }
10940         }
10941         var records = [];
10942         for(var i = 0; i < c; i++){
10943                 var n = root[i];
10944             var values = {};
10945             var id = this.getId(n);
10946             for(var j = 0; j < fl; j++){
10947                 f = fi[j];
10948             var v = this.ef[j](n);
10949             if (!f.convert) {
10950                 Roo.log('missing convert for ' + f.name);
10951                 Roo.log(f);
10952                 continue;
10953             }
10954             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10955             }
10956             var record = new Record(values, id);
10957             record.json = n;
10958             records[i] = record;
10959         }
10960         return {
10961             raw : o,
10962             success : success,
10963             records : records,
10964             totalRecords : totalRecords
10965         };
10966     }
10967 });/*
10968  * Based on:
10969  * Ext JS Library 1.1.1
10970  * Copyright(c) 2006-2007, Ext JS, LLC.
10971  *
10972  * Originally Released Under LGPL - original licence link has changed is not relivant.
10973  *
10974  * Fork - LGPL
10975  * <script type="text/javascript">
10976  */
10977
10978 /**
10979  * @class Roo.data.ArrayReader
10980  * @extends Roo.data.DataReader
10981  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10982  * Each element of that Array represents a row of data fields. The
10983  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10984  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10985  * <p>
10986  * Example code:.
10987  * <pre><code>
10988 var RecordDef = Roo.data.Record.create([
10989     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10990     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10991 ]);
10992 var myReader = new Roo.data.ArrayReader({
10993     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10994 }, RecordDef);
10995 </code></pre>
10996  * <p>
10997  * This would consume an Array like this:
10998  * <pre><code>
10999 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11000   </code></pre>
11001  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11002  * @constructor
11003  * Create a new JsonReader
11004  * @param {Object} meta Metadata configuration options.
11005  * @param {Object} recordType Either an Array of field definition objects
11006  * as specified to {@link Roo.data.Record#create},
11007  * or an {@link Roo.data.Record} object
11008  * created using {@link Roo.data.Record#create}.
11009  */
11010 Roo.data.ArrayReader = function(meta, recordType){
11011     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11012 };
11013
11014 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11015     /**
11016      * Create a data block containing Roo.data.Records from an XML document.
11017      * @param {Object} o An Array of row objects which represents the dataset.
11018      * @return {Object} data A data block which is used by an Roo.data.Store object as
11019      * a cache of Roo.data.Records.
11020      */
11021     readRecords : function(o){
11022         var sid = this.meta ? this.meta.id : null;
11023         var recordType = this.recordType, fields = recordType.prototype.fields;
11024         var records = [];
11025         var root = o;
11026             for(var i = 0; i < root.length; i++){
11027                     var n = root[i];
11028                 var values = {};
11029                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11030                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11031                 var f = fields.items[j];
11032                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11033                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11034                 v = f.convert(v);
11035                 values[f.name] = v;
11036             }
11037                 var record = new recordType(values, id);
11038                 record.json = n;
11039                 records[records.length] = record;
11040             }
11041             return {
11042                 records : records,
11043                 totalRecords : records.length
11044             };
11045     }
11046 });/*
11047  * - LGPL
11048  * * 
11049  */
11050
11051 /**
11052  * @class Roo.bootstrap.ComboBox
11053  * @extends Roo.bootstrap.TriggerField
11054  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11055  * @cfg {Boolean} append (true|false) default false
11056  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11057  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11058  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11059  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11060  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11061  * @cfg {Boolean} animate default true
11062  * @cfg {Boolean} emptyResultText only for touch device
11063  * @constructor
11064  * Create a new ComboBox.
11065  * @param {Object} config Configuration options
11066  */
11067 Roo.bootstrap.ComboBox = function(config){
11068     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11069     this.addEvents({
11070         /**
11071          * @event expand
11072          * Fires when the dropdown list is expanded
11073              * @param {Roo.bootstrap.ComboBox} combo This combo box
11074              */
11075         'expand' : true,
11076         /**
11077          * @event collapse
11078          * Fires when the dropdown list is collapsed
11079              * @param {Roo.bootstrap.ComboBox} combo This combo box
11080              */
11081         'collapse' : true,
11082         /**
11083          * @event beforeselect
11084          * Fires before a list item is selected. Return false to cancel the selection.
11085              * @param {Roo.bootstrap.ComboBox} combo This combo box
11086              * @param {Roo.data.Record} record The data record returned from the underlying store
11087              * @param {Number} index The index of the selected item in the dropdown list
11088              */
11089         'beforeselect' : true,
11090         /**
11091          * @event select
11092          * Fires when a list item is selected
11093              * @param {Roo.bootstrap.ComboBox} combo This combo box
11094              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11095              * @param {Number} index The index of the selected item in the dropdown list
11096              */
11097         'select' : true,
11098         /**
11099          * @event beforequery
11100          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11101          * The event object passed has these properties:
11102              * @param {Roo.bootstrap.ComboBox} combo This combo box
11103              * @param {String} query The query
11104              * @param {Boolean} forceAll true to force "all" query
11105              * @param {Boolean} cancel true to cancel the query
11106              * @param {Object} e The query event object
11107              */
11108         'beforequery': true,
11109          /**
11110          * @event add
11111          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11112              * @param {Roo.bootstrap.ComboBox} combo This combo box
11113              */
11114         'add' : true,
11115         /**
11116          * @event edit
11117          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11118              * @param {Roo.bootstrap.ComboBox} combo This combo box
11119              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11120              */
11121         'edit' : true,
11122         /**
11123          * @event remove
11124          * Fires when the remove value from the combobox array
11125              * @param {Roo.bootstrap.ComboBox} combo This combo box
11126              */
11127         'remove' : true,
11128         /**
11129          * @event specialfilter
11130          * Fires when specialfilter
11131             * @param {Roo.bootstrap.ComboBox} combo This combo box
11132             */
11133         'specialfilter' : true
11134         
11135     });
11136     
11137     this.item = [];
11138     this.tickItems = [];
11139     
11140     this.selectedIndex = -1;
11141     if(this.mode == 'local'){
11142         if(config.queryDelay === undefined){
11143             this.queryDelay = 10;
11144         }
11145         if(config.minChars === undefined){
11146             this.minChars = 0;
11147         }
11148     }
11149 };
11150
11151 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11152      
11153     /**
11154      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11155      * rendering into an Roo.Editor, defaults to false)
11156      */
11157     /**
11158      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11159      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11160      */
11161     /**
11162      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11163      */
11164     /**
11165      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11166      * the dropdown list (defaults to undefined, with no header element)
11167      */
11168
11169      /**
11170      * @cfg {String/Roo.Template} tpl The template to use to render the output
11171      */
11172      
11173      /**
11174      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11175      */
11176     listWidth: undefined,
11177     /**
11178      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11179      * mode = 'remote' or 'text' if mode = 'local')
11180      */
11181     displayField: undefined,
11182     
11183     /**
11184      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11185      * mode = 'remote' or 'value' if mode = 'local'). 
11186      * Note: use of a valueField requires the user make a selection
11187      * in order for a value to be mapped.
11188      */
11189     valueField: undefined,
11190     
11191     
11192     /**
11193      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11194      * field's data value (defaults to the underlying DOM element's name)
11195      */
11196     hiddenName: undefined,
11197     /**
11198      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11199      */
11200     listClass: '',
11201     /**
11202      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11203      */
11204     selectedClass: 'active',
11205     
11206     /**
11207      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11208      */
11209     shadow:'sides',
11210     /**
11211      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11212      * anchor positions (defaults to 'tl-bl')
11213      */
11214     listAlign: 'tl-bl?',
11215     /**
11216      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11217      */
11218     maxHeight: 300,
11219     /**
11220      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11221      * query specified by the allQuery config option (defaults to 'query')
11222      */
11223     triggerAction: 'query',
11224     /**
11225      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11226      * (defaults to 4, does not apply if editable = false)
11227      */
11228     minChars : 4,
11229     /**
11230      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11231      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11232      */
11233     typeAhead: false,
11234     /**
11235      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11236      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11237      */
11238     queryDelay: 500,
11239     /**
11240      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11241      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11242      */
11243     pageSize: 0,
11244     /**
11245      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11246      * when editable = true (defaults to false)
11247      */
11248     selectOnFocus:false,
11249     /**
11250      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11251      */
11252     queryParam: 'query',
11253     /**
11254      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11255      * when mode = 'remote' (defaults to 'Loading...')
11256      */
11257     loadingText: 'Loading...',
11258     /**
11259      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11260      */
11261     resizable: false,
11262     /**
11263      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11264      */
11265     handleHeight : 8,
11266     /**
11267      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11268      * traditional select (defaults to true)
11269      */
11270     editable: true,
11271     /**
11272      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11273      */
11274     allQuery: '',
11275     /**
11276      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11277      */
11278     mode: 'remote',
11279     /**
11280      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11281      * listWidth has a higher value)
11282      */
11283     minListWidth : 70,
11284     /**
11285      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11286      * allow the user to set arbitrary text into the field (defaults to false)
11287      */
11288     forceSelection:false,
11289     /**
11290      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11291      * if typeAhead = true (defaults to 250)
11292      */
11293     typeAheadDelay : 250,
11294     /**
11295      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11296      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11297      */
11298     valueNotFoundText : undefined,
11299     /**
11300      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11301      */
11302     blockFocus : false,
11303     
11304     /**
11305      * @cfg {Boolean} disableClear Disable showing of clear button.
11306      */
11307     disableClear : false,
11308     /**
11309      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11310      */
11311     alwaysQuery : false,
11312     
11313     /**
11314      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11315      */
11316     multiple : false,
11317     
11318     /**
11319      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11320      */
11321     invalidClass : "has-warning",
11322     
11323     /**
11324      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11325      */
11326     validClass : "has-success",
11327     
11328     /**
11329      * @cfg {Boolean} specialFilter (true|false) special filter default false
11330      */
11331     specialFilter : false,
11332     
11333     /**
11334      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11335      */
11336     mobileTouchView : true,
11337     
11338     //private
11339     addicon : false,
11340     editicon: false,
11341     
11342     page: 0,
11343     hasQuery: false,
11344     append: false,
11345     loadNext: false,
11346     autoFocus : true,
11347     tickable : false,
11348     btnPosition : 'right',
11349     triggerList : true,
11350     showToggleBtn : true,
11351     animate : true,
11352     emptyResultText: 'Empty',
11353     // element that contains real text value.. (when hidden is used..)
11354     
11355     getAutoCreate : function()
11356     {
11357         var cfg = false;
11358         
11359         /*
11360          * Touch Devices
11361          */
11362         
11363         if(Roo.isTouch && this.mobileTouchView){
11364             cfg = this.getAutoCreateTouchView();
11365             return cfg;;
11366         }
11367         
11368         /*
11369          *  Normal ComboBox
11370          */
11371         if(!this.tickable){
11372             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11373             return cfg;
11374         }
11375         
11376         /*
11377          *  ComboBox with tickable selections
11378          */
11379              
11380         var align = this.labelAlign || this.parentLabelAlign();
11381         
11382         cfg = {
11383             cls : 'form-group roo-combobox-tickable' //input-group
11384         };
11385         
11386         var buttons = {
11387             tag : 'div',
11388             cls : 'tickable-buttons',
11389             cn : [
11390                 {
11391                     tag : 'button',
11392                     type : 'button',
11393                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11394                     html : 'Edit'
11395                 },
11396                 {
11397                     tag : 'button',
11398                     type : 'button',
11399                     name : 'ok',
11400                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11401                     html : 'Done'
11402                 },
11403                 {
11404                     tag : 'button',
11405                     type : 'button',
11406                     name : 'cancel',
11407                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11408                     html : 'Cancel'
11409                 }
11410             ]
11411         };
11412         
11413         if(this.editable){
11414             buttons.cn.unshift({
11415                 tag: 'input',
11416                 cls: 'select2-search-field-input'
11417             });
11418         }
11419         
11420         var _this = this;
11421         
11422         Roo.each(buttons.cn, function(c){
11423             if (_this.size) {
11424                 c.cls += ' btn-' + _this.size;
11425             }
11426
11427             if (_this.disabled) {
11428                 c.disabled = true;
11429             }
11430         });
11431         
11432         var box = {
11433             tag: 'div',
11434             cn: [
11435                 {
11436                     tag: 'input',
11437                     type : 'hidden',
11438                     cls: 'form-hidden-field'
11439                 },
11440                 {
11441                     tag: 'ul',
11442                     cls: 'select2-choices',
11443                     cn:[
11444                         {
11445                             tag: 'li',
11446                             cls: 'select2-search-field',
11447                             cn: [
11448
11449                                 buttons
11450                             ]
11451                         }
11452                     ]
11453                 }
11454             ]
11455         }
11456         
11457         var combobox = {
11458             cls: 'select2-container input-group select2-container-multi',
11459             cn: [
11460                 box
11461 //                {
11462 //                    tag: 'ul',
11463 //                    cls: 'typeahead typeahead-long dropdown-menu',
11464 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11465 //                }
11466             ]
11467         };
11468         
11469         if(this.hasFeedback && !this.allowBlank){
11470             
11471             var feedback = {
11472                 tag: 'span',
11473                 cls: 'glyphicon form-control-feedback'
11474             };
11475
11476             combobox.cn.push(feedback);
11477         }
11478         
11479         if (align ==='left' && this.fieldLabel.length) {
11480             
11481                 Roo.log("left and has label");
11482                 cfg.cn = [
11483                     
11484                     {
11485                         tag: 'label',
11486                         'for' :  id,
11487                         cls : 'control-label col-sm-' + this.labelWidth,
11488                         html : this.fieldLabel
11489                         
11490                     },
11491                     {
11492                         cls : "col-sm-" + (12 - this.labelWidth), 
11493                         cn: [
11494                             combobox
11495                         ]
11496                     }
11497                     
11498                 ];
11499         } else if ( this.fieldLabel.length) {
11500                 Roo.log(" label");
11501                  cfg.cn = [
11502                    
11503                     {
11504                         tag: 'label',
11505                         //cls : 'input-group-addon',
11506                         html : this.fieldLabel
11507                         
11508                     },
11509                     
11510                     combobox
11511                     
11512                 ];
11513
11514         } else {
11515             
11516                 Roo.log(" no label && no align");
11517                 cfg = combobox
11518                      
11519                 
11520         }
11521          
11522         var settings=this;
11523         ['xs','sm','md','lg'].map(function(size){
11524             if (settings[size]) {
11525                 cfg.cls += ' col-' + size + '-' + settings[size];
11526             }
11527         });
11528         
11529         return cfg;
11530         
11531     },
11532     
11533     // private
11534     initEvents: function()
11535     {
11536         
11537         if (!this.store) {
11538             throw "can not find store for combo";
11539         }
11540         
11541         this.store = Roo.factory(this.store, Roo.data);
11542         
11543         /*
11544          * Touch Devices
11545          */
11546         
11547         if(Roo.isTouch && this.mobileTouchView){
11548             this.initTouchView();
11549             return;
11550         }
11551         
11552         if(this.tickable){
11553             this.initTickableEvents();
11554             return;
11555         }
11556         
11557         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11558         
11559         if(this.hiddenName){
11560             
11561             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11562             
11563             this.hiddenField.dom.value =
11564                 this.hiddenValue !== undefined ? this.hiddenValue :
11565                 this.value !== undefined ? this.value : '';
11566
11567             // prevent input submission
11568             this.el.dom.removeAttribute('name');
11569             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11570              
11571              
11572         }
11573         //if(Roo.isGecko){
11574         //    this.el.dom.setAttribute('autocomplete', 'off');
11575         //}
11576         
11577         var cls = 'x-combo-list';
11578         
11579         //this.list = new Roo.Layer({
11580         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11581         //});
11582         
11583         var _this = this;
11584         
11585         (function(){
11586             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11587             _this.list.setWidth(lw);
11588         }).defer(100);
11589         
11590         this.list.on('mouseover', this.onViewOver, this);
11591         this.list.on('mousemove', this.onViewMove, this);
11592         
11593         this.list.on('scroll', this.onViewScroll, this);
11594         
11595         /*
11596         this.list.swallowEvent('mousewheel');
11597         this.assetHeight = 0;
11598
11599         if(this.title){
11600             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11601             this.assetHeight += this.header.getHeight();
11602         }
11603
11604         this.innerList = this.list.createChild({cls:cls+'-inner'});
11605         this.innerList.on('mouseover', this.onViewOver, this);
11606         this.innerList.on('mousemove', this.onViewMove, this);
11607         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11608         
11609         if(this.allowBlank && !this.pageSize && !this.disableClear){
11610             this.footer = this.list.createChild({cls:cls+'-ft'});
11611             this.pageTb = new Roo.Toolbar(this.footer);
11612            
11613         }
11614         if(this.pageSize){
11615             this.footer = this.list.createChild({cls:cls+'-ft'});
11616             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11617                     {pageSize: this.pageSize});
11618             
11619         }
11620         
11621         if (this.pageTb && this.allowBlank && !this.disableClear) {
11622             var _this = this;
11623             this.pageTb.add(new Roo.Toolbar.Fill(), {
11624                 cls: 'x-btn-icon x-btn-clear',
11625                 text: '&#160;',
11626                 handler: function()
11627                 {
11628                     _this.collapse();
11629                     _this.clearValue();
11630                     _this.onSelect(false, -1);
11631                 }
11632             });
11633         }
11634         if (this.footer) {
11635             this.assetHeight += this.footer.getHeight();
11636         }
11637         */
11638             
11639         if(!this.tpl){
11640             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11641         }
11642
11643         this.view = new Roo.View(this.list, this.tpl, {
11644             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11645         });
11646         //this.view.wrapEl.setDisplayed(false);
11647         this.view.on('click', this.onViewClick, this);
11648         
11649         
11650         
11651         this.store.on('beforeload', this.onBeforeLoad, this);
11652         this.store.on('load', this.onLoad, this);
11653         this.store.on('loadexception', this.onLoadException, this);
11654         /*
11655         if(this.resizable){
11656             this.resizer = new Roo.Resizable(this.list,  {
11657                pinned:true, handles:'se'
11658             });
11659             this.resizer.on('resize', function(r, w, h){
11660                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11661                 this.listWidth = w;
11662                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11663                 this.restrictHeight();
11664             }, this);
11665             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11666         }
11667         */
11668         if(!this.editable){
11669             this.editable = true;
11670             this.setEditable(false);
11671         }
11672         
11673         /*
11674         
11675         if (typeof(this.events.add.listeners) != 'undefined') {
11676             
11677             this.addicon = this.wrap.createChild(
11678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11679        
11680             this.addicon.on('click', function(e) {
11681                 this.fireEvent('add', this);
11682             }, this);
11683         }
11684         if (typeof(this.events.edit.listeners) != 'undefined') {
11685             
11686             this.editicon = this.wrap.createChild(
11687                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11688             if (this.addicon) {
11689                 this.editicon.setStyle('margin-left', '40px');
11690             }
11691             this.editicon.on('click', function(e) {
11692                 
11693                 // we fire even  if inothing is selected..
11694                 this.fireEvent('edit', this, this.lastData );
11695                 
11696             }, this);
11697         }
11698         */
11699         
11700         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11701             "up" : function(e){
11702                 this.inKeyMode = true;
11703                 this.selectPrev();
11704             },
11705
11706             "down" : function(e){
11707                 if(!this.isExpanded()){
11708                     this.onTriggerClick();
11709                 }else{
11710                     this.inKeyMode = true;
11711                     this.selectNext();
11712                 }
11713             },
11714
11715             "enter" : function(e){
11716 //                this.onViewClick();
11717                 //return true;
11718                 this.collapse();
11719                 
11720                 if(this.fireEvent("specialkey", this, e)){
11721                     this.onViewClick(false);
11722                 }
11723                 
11724                 return true;
11725             },
11726
11727             "esc" : function(e){
11728                 this.collapse();
11729             },
11730
11731             "tab" : function(e){
11732                 this.collapse();
11733                 
11734                 if(this.fireEvent("specialkey", this, e)){
11735                     this.onViewClick(false);
11736                 }
11737                 
11738                 return true;
11739             },
11740
11741             scope : this,
11742
11743             doRelay : function(foo, bar, hname){
11744                 if(hname == 'down' || this.scope.isExpanded()){
11745                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11746                 }
11747                 return true;
11748             },
11749
11750             forceKeyDown: true
11751         });
11752         
11753         
11754         this.queryDelay = Math.max(this.queryDelay || 10,
11755                 this.mode == 'local' ? 10 : 250);
11756         
11757         
11758         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11759         
11760         if(this.typeAhead){
11761             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11762         }
11763         if(this.editable !== false){
11764             this.inputEl().on("keyup", this.onKeyUp, this);
11765         }
11766         if(this.forceSelection){
11767             this.inputEl().on('blur', this.doForce, this);
11768         }
11769         
11770         if(this.multiple){
11771             this.choices = this.el.select('ul.select2-choices', true).first();
11772             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11773         }
11774     },
11775     
11776     initTickableEvents: function()
11777     {   
11778         this.createList();
11779         
11780         if(this.hiddenName){
11781             
11782             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11783             
11784             this.hiddenField.dom.value =
11785                 this.hiddenValue !== undefined ? this.hiddenValue :
11786                 this.value !== undefined ? this.value : '';
11787
11788             // prevent input submission
11789             this.el.dom.removeAttribute('name');
11790             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11791              
11792              
11793         }
11794         
11795 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11796         
11797         this.choices = this.el.select('ul.select2-choices', true).first();
11798         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11799         if(this.triggerList){
11800             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11801         }
11802          
11803         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11804         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11805         
11806         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11807         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11808         
11809         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11810         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11811         
11812         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11813         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11814         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11815         
11816         this.okBtn.hide();
11817         this.cancelBtn.hide();
11818         
11819         var _this = this;
11820         
11821         (function(){
11822             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11823             _this.list.setWidth(lw);
11824         }).defer(100);
11825         
11826         this.list.on('mouseover', this.onViewOver, this);
11827         this.list.on('mousemove', this.onViewMove, this);
11828         
11829         this.list.on('scroll', this.onViewScroll, this);
11830         
11831         if(!this.tpl){
11832             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>';
11833         }
11834
11835         this.view = new Roo.View(this.list, this.tpl, {
11836             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11837         });
11838         
11839         //this.view.wrapEl.setDisplayed(false);
11840         this.view.on('click', this.onViewClick, this);
11841         
11842         
11843         
11844         this.store.on('beforeload', this.onBeforeLoad, this);
11845         this.store.on('load', this.onLoad, this);
11846         this.store.on('loadexception', this.onLoadException, this);
11847         
11848         if(this.editable){
11849             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11850                 "up" : function(e){
11851                     this.inKeyMode = true;
11852                     this.selectPrev();
11853                 },
11854
11855                 "down" : function(e){
11856                     this.inKeyMode = true;
11857                     this.selectNext();
11858                 },
11859
11860                 "enter" : function(e){
11861                     if(this.fireEvent("specialkey", this, e)){
11862                         this.onViewClick(false);
11863                     }
11864                     
11865                     return true;
11866                 },
11867
11868                 "esc" : function(e){
11869                     this.onTickableFooterButtonClick(e, false, false);
11870                 },
11871
11872                 "tab" : function(e){
11873                     this.fireEvent("specialkey", this, e);
11874                     
11875                     this.onTickableFooterButtonClick(e, false, false);
11876                     
11877                     return true;
11878                 },
11879
11880                 scope : this,
11881
11882                 doRelay : function(e, fn, key){
11883                     if(this.scope.isExpanded()){
11884                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11885                     }
11886                     return true;
11887                 },
11888
11889                 forceKeyDown: true
11890             });
11891         }
11892         
11893         this.queryDelay = Math.max(this.queryDelay || 10,
11894                 this.mode == 'local' ? 10 : 250);
11895         
11896         
11897         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11898         
11899         if(this.typeAhead){
11900             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11901         }
11902         
11903         if(this.editable !== false){
11904             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11905         }
11906         
11907     },
11908
11909     onDestroy : function(){
11910         if(this.view){
11911             this.view.setStore(null);
11912             this.view.el.removeAllListeners();
11913             this.view.el.remove();
11914             this.view.purgeListeners();
11915         }
11916         if(this.list){
11917             this.list.dom.innerHTML  = '';
11918         }
11919         
11920         if(this.store){
11921             this.store.un('beforeload', this.onBeforeLoad, this);
11922             this.store.un('load', this.onLoad, this);
11923             this.store.un('loadexception', this.onLoadException, this);
11924         }
11925         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11926     },
11927
11928     // private
11929     fireKey : function(e){
11930         if(e.isNavKeyPress() && !this.list.isVisible()){
11931             this.fireEvent("specialkey", this, e);
11932         }
11933     },
11934
11935     // private
11936     onResize: function(w, h){
11937 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11938 //        
11939 //        if(typeof w != 'number'){
11940 //            // we do not handle it!?!?
11941 //            return;
11942 //        }
11943 //        var tw = this.trigger.getWidth();
11944 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11945 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11946 //        var x = w - tw;
11947 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11948 //            
11949 //        //this.trigger.setStyle('left', x+'px');
11950 //        
11951 //        if(this.list && this.listWidth === undefined){
11952 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11953 //            this.list.setWidth(lw);
11954 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11955 //        }
11956         
11957     
11958         
11959     },
11960
11961     /**
11962      * Allow or prevent the user from directly editing the field text.  If false is passed,
11963      * the user will only be able to select from the items defined in the dropdown list.  This method
11964      * is the runtime equivalent of setting the 'editable' config option at config time.
11965      * @param {Boolean} value True to allow the user to directly edit the field text
11966      */
11967     setEditable : function(value){
11968         if(value == this.editable){
11969             return;
11970         }
11971         this.editable = value;
11972         if(!value){
11973             this.inputEl().dom.setAttribute('readOnly', true);
11974             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11975             this.inputEl().addClass('x-combo-noedit');
11976         }else{
11977             this.inputEl().dom.setAttribute('readOnly', false);
11978             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11979             this.inputEl().removeClass('x-combo-noedit');
11980         }
11981     },
11982
11983     // private
11984     
11985     onBeforeLoad : function(combo,opts){
11986         if(!this.hasFocus){
11987             return;
11988         }
11989          if (!opts.add) {
11990             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11991          }
11992         this.restrictHeight();
11993         this.selectedIndex = -1;
11994     },
11995
11996     // private
11997     onLoad : function(){
11998         
11999         this.hasQuery = false;
12000         
12001         if(!this.hasFocus){
12002             return;
12003         }
12004         
12005         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12006             this.loading.hide();
12007         }
12008              
12009         if(this.store.getCount() > 0){
12010             this.expand();
12011             this.restrictHeight();
12012             if(this.lastQuery == this.allQuery){
12013                 if(this.editable && !this.tickable){
12014                     this.inputEl().dom.select();
12015                 }
12016                 
12017                 if(
12018                     !this.selectByValue(this.value, true) &&
12019                     this.autoFocus && 
12020                     (
12021                         !this.store.lastOptions ||
12022                         typeof(this.store.lastOptions.add) == 'undefined' || 
12023                         this.store.lastOptions.add != true
12024                     )
12025                 ){
12026                     this.select(0, true);
12027                 }
12028             }else{
12029                 if(this.autoFocus){
12030                     this.selectNext();
12031                 }
12032                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12033                     this.taTask.delay(this.typeAheadDelay);
12034                 }
12035             }
12036         }else{
12037             this.onEmptyResults();
12038         }
12039         
12040         //this.el.focus();
12041     },
12042     // private
12043     onLoadException : function()
12044     {
12045         this.hasQuery = false;
12046         
12047         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12048             this.loading.hide();
12049         }
12050         
12051         if(this.tickable && this.editable){
12052             return;
12053         }
12054         
12055         this.collapse();
12056         
12057         Roo.log(this.store.reader.jsonData);
12058         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12059             // fixme
12060             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12061         }
12062         
12063         
12064     },
12065     // private
12066     onTypeAhead : function(){
12067         if(this.store.getCount() > 0){
12068             var r = this.store.getAt(0);
12069             var newValue = r.data[this.displayField];
12070             var len = newValue.length;
12071             var selStart = this.getRawValue().length;
12072             
12073             if(selStart != len){
12074                 this.setRawValue(newValue);
12075                 this.selectText(selStart, newValue.length);
12076             }
12077         }
12078     },
12079
12080     // private
12081     onSelect : function(record, index){
12082         
12083         if(this.fireEvent('beforeselect', this, record, index) !== false){
12084         
12085             this.setFromData(index > -1 ? record.data : false);
12086             
12087             this.collapse();
12088             this.fireEvent('select', this, record, index);
12089         }
12090     },
12091
12092     /**
12093      * Returns the currently selected field value or empty string if no value is set.
12094      * @return {String} value The selected value
12095      */
12096     getValue : function(){
12097         
12098         if(this.multiple){
12099             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12100         }
12101         
12102         if(this.valueField){
12103             return typeof this.value != 'undefined' ? this.value : '';
12104         }else{
12105             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12106         }
12107     },
12108
12109     /**
12110      * Clears any text/value currently set in the field
12111      */
12112     clearValue : function(){
12113         if(this.hiddenField){
12114             this.hiddenField.dom.value = '';
12115         }
12116         this.value = '';
12117         this.setRawValue('');
12118         this.lastSelectionText = '';
12119         this.lastData = false;
12120         
12121         var close = this.closeTriggerEl();
12122         
12123         if(close){
12124             close.hide();
12125         }
12126         
12127     },
12128
12129     /**
12130      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12131      * will be displayed in the field.  If the value does not match the data value of an existing item,
12132      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12133      * Otherwise the field will be blank (although the value will still be set).
12134      * @param {String} value The value to match
12135      */
12136     setValue : function(v){
12137         if(this.multiple){
12138             this.syncValue();
12139             return;
12140         }
12141         
12142         var text = v;
12143         if(this.valueField){
12144             var r = this.findRecord(this.valueField, v);
12145             if(r){
12146                 text = r.data[this.displayField];
12147             }else if(this.valueNotFoundText !== undefined){
12148                 text = this.valueNotFoundText;
12149             }
12150         }
12151         this.lastSelectionText = text;
12152         if(this.hiddenField){
12153             this.hiddenField.dom.value = v;
12154         }
12155         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12156         this.value = v;
12157         
12158         var close = this.closeTriggerEl();
12159         
12160         if(close){
12161             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12162         }
12163     },
12164     /**
12165      * @property {Object} the last set data for the element
12166      */
12167     
12168     lastData : false,
12169     /**
12170      * Sets the value of the field based on a object which is related to the record format for the store.
12171      * @param {Object} value the value to set as. or false on reset?
12172      */
12173     setFromData : function(o){
12174         
12175         if(this.multiple){
12176             this.addItem(o);
12177             return;
12178         }
12179             
12180         var dv = ''; // display value
12181         var vv = ''; // value value..
12182         this.lastData = o;
12183         if (this.displayField) {
12184             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12185         } else {
12186             // this is an error condition!!!
12187             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12188         }
12189         
12190         if(this.valueField){
12191             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12192         }
12193         
12194         var close = this.closeTriggerEl();
12195         
12196         if(close){
12197             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12198         }
12199         
12200         if(this.hiddenField){
12201             this.hiddenField.dom.value = vv;
12202             
12203             this.lastSelectionText = dv;
12204             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12205             this.value = vv;
12206             return;
12207         }
12208         // no hidden field.. - we store the value in 'value', but still display
12209         // display field!!!!
12210         this.lastSelectionText = dv;
12211         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12212         this.value = vv;
12213         
12214         
12215         
12216     },
12217     // private
12218     reset : function(){
12219         // overridden so that last data is reset..
12220         
12221         if(this.multiple){
12222             this.clearItem();
12223             return;
12224         }
12225         
12226         this.setValue(this.originalValue);
12227         this.clearInvalid();
12228         this.lastData = false;
12229         if (this.view) {
12230             this.view.clearSelections();
12231         }
12232     },
12233     // private
12234     findRecord : function(prop, value){
12235         var record;
12236         if(this.store.getCount() > 0){
12237             this.store.each(function(r){
12238                 if(r.data[prop] == value){
12239                     record = r;
12240                     return false;
12241                 }
12242                 return true;
12243             });
12244         }
12245         return record;
12246     },
12247     
12248     getName: function()
12249     {
12250         // returns hidden if it's set..
12251         if (!this.rendered) {return ''};
12252         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12253         
12254     },
12255     // private
12256     onViewMove : function(e, t){
12257         this.inKeyMode = false;
12258     },
12259
12260     // private
12261     onViewOver : function(e, t){
12262         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12263             return;
12264         }
12265         var item = this.view.findItemFromChild(t);
12266         
12267         if(item){
12268             var index = this.view.indexOf(item);
12269             this.select(index, false);
12270         }
12271     },
12272
12273     // private
12274     onViewClick : function(view, doFocus, el, e)
12275     {
12276         var index = this.view.getSelectedIndexes()[0];
12277         
12278         var r = this.store.getAt(index);
12279         
12280         if(this.tickable){
12281             
12282             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12283                 return;
12284             }
12285             
12286             var rm = false;
12287             var _this = this;
12288             
12289             Roo.each(this.tickItems, function(v,k){
12290                 
12291                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12292                     _this.tickItems.splice(k, 1);
12293                     
12294                     if(typeof(e) == 'undefined' && view == false){
12295                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12296                     }
12297                     
12298                     rm = true;
12299                     return;
12300                 }
12301             });
12302             
12303             if(rm){
12304                 return;
12305             }
12306             
12307             this.tickItems.push(r.data);
12308             
12309             if(typeof(e) == 'undefined' && view == false){
12310                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12311             }
12312                     
12313             return;
12314         }
12315         
12316         if(r){
12317             this.onSelect(r, index);
12318         }
12319         if(doFocus !== false && !this.blockFocus){
12320             this.inputEl().focus();
12321         }
12322     },
12323
12324     // private
12325     restrictHeight : function(){
12326         //this.innerList.dom.style.height = '';
12327         //var inner = this.innerList.dom;
12328         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12329         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12330         //this.list.beginUpdate();
12331         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12332         this.list.alignTo(this.inputEl(), this.listAlign);
12333         this.list.alignTo(this.inputEl(), this.listAlign);
12334         //this.list.endUpdate();
12335     },
12336
12337     // private
12338     onEmptyResults : function(){
12339         
12340         if(this.tickable && this.editable){
12341             this.restrictHeight();
12342             return;
12343         }
12344         
12345         this.collapse();
12346     },
12347
12348     /**
12349      * Returns true if the dropdown list is expanded, else false.
12350      */
12351     isExpanded : function(){
12352         return this.list.isVisible();
12353     },
12354
12355     /**
12356      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12357      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12358      * @param {String} value The data value of the item to select
12359      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12360      * selected item if it is not currently in view (defaults to true)
12361      * @return {Boolean} True if the value matched an item in the list, else false
12362      */
12363     selectByValue : function(v, scrollIntoView){
12364         if(v !== undefined && v !== null){
12365             var r = this.findRecord(this.valueField || this.displayField, v);
12366             if(r){
12367                 this.select(this.store.indexOf(r), scrollIntoView);
12368                 return true;
12369             }
12370         }
12371         return false;
12372     },
12373
12374     /**
12375      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12376      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12377      * @param {Number} index The zero-based index of the list item to select
12378      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12379      * selected item if it is not currently in view (defaults to true)
12380      */
12381     select : function(index, scrollIntoView){
12382         this.selectedIndex = index;
12383         this.view.select(index);
12384         if(scrollIntoView !== false){
12385             var el = this.view.getNode(index);
12386             /*
12387              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12388              */
12389             if(el){
12390                 this.list.scrollChildIntoView(el, false);
12391             }
12392         }
12393     },
12394
12395     // private
12396     selectNext : function(){
12397         var ct = this.store.getCount();
12398         if(ct > 0){
12399             if(this.selectedIndex == -1){
12400                 this.select(0);
12401             }else if(this.selectedIndex < ct-1){
12402                 this.select(this.selectedIndex+1);
12403             }
12404         }
12405     },
12406
12407     // private
12408     selectPrev : function(){
12409         var ct = this.store.getCount();
12410         if(ct > 0){
12411             if(this.selectedIndex == -1){
12412                 this.select(0);
12413             }else if(this.selectedIndex != 0){
12414                 this.select(this.selectedIndex-1);
12415             }
12416         }
12417     },
12418
12419     // private
12420     onKeyUp : function(e){
12421         if(this.editable !== false && !e.isSpecialKey()){
12422             this.lastKey = e.getKey();
12423             this.dqTask.delay(this.queryDelay);
12424         }
12425     },
12426
12427     // private
12428     validateBlur : function(){
12429         return !this.list || !this.list.isVisible();   
12430     },
12431
12432     // private
12433     initQuery : function(){
12434         
12435         var v = this.getRawValue();
12436         
12437         if(this.tickable && this.editable){
12438             v = this.tickableInputEl().getValue();
12439         }
12440         
12441         this.doQuery(v);
12442     },
12443
12444     // private
12445     doForce : function(){
12446         if(this.inputEl().dom.value.length > 0){
12447             this.inputEl().dom.value =
12448                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12449              
12450         }
12451     },
12452
12453     /**
12454      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12455      * query allowing the query action to be canceled if needed.
12456      * @param {String} query The SQL query to execute
12457      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12458      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12459      * saved in the current store (defaults to false)
12460      */
12461     doQuery : function(q, forceAll){
12462         
12463         if(q === undefined || q === null){
12464             q = '';
12465         }
12466         var qe = {
12467             query: q,
12468             forceAll: forceAll,
12469             combo: this,
12470             cancel:false
12471         };
12472         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12473             return false;
12474         }
12475         q = qe.query;
12476         
12477         forceAll = qe.forceAll;
12478         if(forceAll === true || (q.length >= this.minChars)){
12479             
12480             this.hasQuery = true;
12481             
12482             if(this.lastQuery != q || this.alwaysQuery){
12483                 this.lastQuery = q;
12484                 if(this.mode == 'local'){
12485                     this.selectedIndex = -1;
12486                     if(forceAll){
12487                         this.store.clearFilter();
12488                     }else{
12489                         
12490                         if(this.specialFilter){
12491                             this.fireEvent('specialfilter', this);
12492                             this.onLoad();
12493                             return;
12494                         }
12495                         
12496                         this.store.filter(this.displayField, q);
12497                     }
12498                     
12499                     this.store.fireEvent("datachanged", this.store);
12500                     
12501                     this.onLoad();
12502                     
12503                     
12504                 }else{
12505                     
12506                     this.store.baseParams[this.queryParam] = q;
12507                     
12508                     var options = {params : this.getParams(q)};
12509                     
12510                     if(this.loadNext){
12511                         options.add = true;
12512                         options.params.start = this.page * this.pageSize;
12513                     }
12514                     
12515                     this.store.load(options);
12516                     
12517                     /*
12518                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12519                      *  we should expand the list on onLoad
12520                      *  so command out it
12521                      */
12522 //                    this.expand();
12523                 }
12524             }else{
12525                 this.selectedIndex = -1;
12526                 this.onLoad();   
12527             }
12528         }
12529         
12530         this.loadNext = false;
12531     },
12532     
12533     // private
12534     getParams : function(q){
12535         var p = {};
12536         //p[this.queryParam] = q;
12537         
12538         if(this.pageSize){
12539             p.start = 0;
12540             p.limit = this.pageSize;
12541         }
12542         return p;
12543     },
12544
12545     /**
12546      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12547      */
12548     collapse : function(){
12549         if(!this.isExpanded()){
12550             return;
12551         }
12552         
12553         this.list.hide();
12554         
12555         if(this.tickable){
12556             this.hasFocus = false;
12557             this.okBtn.hide();
12558             this.cancelBtn.hide();
12559             this.trigger.show();
12560             
12561             if(this.editable){
12562                 this.tickableInputEl().dom.value = '';
12563                 this.tickableInputEl().blur();
12564             }
12565             
12566         }
12567         
12568         Roo.get(document).un('mousedown', this.collapseIf, this);
12569         Roo.get(document).un('mousewheel', this.collapseIf, this);
12570         if (!this.editable) {
12571             Roo.get(document).un('keydown', this.listKeyPress, this);
12572         }
12573         this.fireEvent('collapse', this);
12574     },
12575
12576     // private
12577     collapseIf : function(e){
12578         var in_combo  = e.within(this.el);
12579         var in_list =  e.within(this.list);
12580         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12581         
12582         if (in_combo || in_list || is_list) {
12583             //e.stopPropagation();
12584             return;
12585         }
12586         
12587         if(this.tickable){
12588             this.onTickableFooterButtonClick(e, false, false);
12589         }
12590
12591         this.collapse();
12592         
12593     },
12594
12595     /**
12596      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12597      */
12598     expand : function(){
12599        
12600         if(this.isExpanded() || !this.hasFocus){
12601             return;
12602         }
12603         
12604         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12605         this.list.setWidth(lw);
12606         
12607         
12608          Roo.log('expand');
12609         
12610         this.list.show();
12611         
12612         this.restrictHeight();
12613         
12614         if(this.tickable){
12615             
12616             this.tickItems = Roo.apply([], this.item);
12617             
12618             this.okBtn.show();
12619             this.cancelBtn.show();
12620             this.trigger.hide();
12621             
12622             if(this.editable){
12623                 this.tickableInputEl().focus();
12624             }
12625             
12626         }
12627         
12628         Roo.get(document).on('mousedown', this.collapseIf, this);
12629         Roo.get(document).on('mousewheel', this.collapseIf, this);
12630         if (!this.editable) {
12631             Roo.get(document).on('keydown', this.listKeyPress, this);
12632         }
12633         
12634         this.fireEvent('expand', this);
12635     },
12636
12637     // private
12638     // Implements the default empty TriggerField.onTriggerClick function
12639     onTriggerClick : function(e)
12640     {
12641         Roo.log('trigger click');
12642         
12643         if(this.disabled || !this.triggerList){
12644             return;
12645         }
12646         
12647         this.page = 0;
12648         this.loadNext = false;
12649         
12650         if(this.isExpanded()){
12651             this.collapse();
12652             if (!this.blockFocus) {
12653                 this.inputEl().focus();
12654             }
12655             
12656         }else {
12657             this.hasFocus = true;
12658             if(this.triggerAction == 'all') {
12659                 this.doQuery(this.allQuery, true);
12660             } else {
12661                 this.doQuery(this.getRawValue());
12662             }
12663             if (!this.blockFocus) {
12664                 this.inputEl().focus();
12665             }
12666         }
12667     },
12668     
12669     onTickableTriggerClick : function(e)
12670     {
12671         if(this.disabled){
12672             return;
12673         }
12674         
12675         this.page = 0;
12676         this.loadNext = false;
12677         this.hasFocus = true;
12678         
12679         if(this.triggerAction == 'all') {
12680             this.doQuery(this.allQuery, true);
12681         } else {
12682             this.doQuery(this.getRawValue());
12683         }
12684     },
12685     
12686     onSearchFieldClick : function(e)
12687     {
12688         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12689             this.onTickableFooterButtonClick(e, false, false);
12690             return;
12691         }
12692         
12693         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12694             return;
12695         }
12696         
12697         this.page = 0;
12698         this.loadNext = false;
12699         this.hasFocus = true;
12700         
12701         if(this.triggerAction == 'all') {
12702             this.doQuery(this.allQuery, true);
12703         } else {
12704             this.doQuery(this.getRawValue());
12705         }
12706     },
12707     
12708     listKeyPress : function(e)
12709     {
12710         //Roo.log('listkeypress');
12711         // scroll to first matching element based on key pres..
12712         if (e.isSpecialKey()) {
12713             return false;
12714         }
12715         var k = String.fromCharCode(e.getKey()).toUpperCase();
12716         //Roo.log(k);
12717         var match  = false;
12718         var csel = this.view.getSelectedNodes();
12719         var cselitem = false;
12720         if (csel.length) {
12721             var ix = this.view.indexOf(csel[0]);
12722             cselitem  = this.store.getAt(ix);
12723             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12724                 cselitem = false;
12725             }
12726             
12727         }
12728         
12729         this.store.each(function(v) { 
12730             if (cselitem) {
12731                 // start at existing selection.
12732                 if (cselitem.id == v.id) {
12733                     cselitem = false;
12734                 }
12735                 return true;
12736             }
12737                 
12738             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12739                 match = this.store.indexOf(v);
12740                 return false;
12741             }
12742             return true;
12743         }, this);
12744         
12745         if (match === false) {
12746             return true; // no more action?
12747         }
12748         // scroll to?
12749         this.view.select(match);
12750         var sn = Roo.get(this.view.getSelectedNodes()[0])
12751         sn.scrollIntoView(sn.dom.parentNode, false);
12752     },
12753     
12754     onViewScroll : function(e, t){
12755         
12756         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){
12757             return;
12758         }
12759         
12760         this.hasQuery = true;
12761         
12762         this.loading = this.list.select('.loading', true).first();
12763         
12764         if(this.loading === null){
12765             this.list.createChild({
12766                 tag: 'div',
12767                 cls: 'loading select2-more-results select2-active',
12768                 html: 'Loading more results...'
12769             })
12770             
12771             this.loading = this.list.select('.loading', true).first();
12772             
12773             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12774             
12775             this.loading.hide();
12776         }
12777         
12778         this.loading.show();
12779         
12780         var _combo = this;
12781         
12782         this.page++;
12783         this.loadNext = true;
12784         
12785         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12786         
12787         return;
12788     },
12789     
12790     addItem : function(o)
12791     {   
12792         var dv = ''; // display value
12793         
12794         if (this.displayField) {
12795             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12796         } else {
12797             // this is an error condition!!!
12798             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12799         }
12800         
12801         if(!dv.length){
12802             return;
12803         }
12804         
12805         var choice = this.choices.createChild({
12806             tag: 'li',
12807             cls: 'select2-search-choice',
12808             cn: [
12809                 {
12810                     tag: 'div',
12811                     html: dv
12812                 },
12813                 {
12814                     tag: 'a',
12815                     href: '#',
12816                     cls: 'select2-search-choice-close',
12817                     tabindex: '-1'
12818                 }
12819             ]
12820             
12821         }, this.searchField);
12822         
12823         var close = choice.select('a.select2-search-choice-close', true).first()
12824         
12825         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12826         
12827         this.item.push(o);
12828         
12829         this.lastData = o;
12830         
12831         this.syncValue();
12832         
12833         this.inputEl().dom.value = '';
12834         
12835         this.validate();
12836     },
12837     
12838     onRemoveItem : function(e, _self, o)
12839     {
12840         e.preventDefault();
12841         
12842         this.lastItem = Roo.apply([], this.item);
12843         
12844         var index = this.item.indexOf(o.data) * 1;
12845         
12846         if( index < 0){
12847             Roo.log('not this item?!');
12848             return;
12849         }
12850         
12851         this.item.splice(index, 1);
12852         o.item.remove();
12853         
12854         this.syncValue();
12855         
12856         this.fireEvent('remove', this, e);
12857         
12858         this.validate();
12859         
12860     },
12861     
12862     syncValue : function()
12863     {
12864         if(!this.item.length){
12865             this.clearValue();
12866             return;
12867         }
12868             
12869         var value = [];
12870         var _this = this;
12871         Roo.each(this.item, function(i){
12872             if(_this.valueField){
12873                 value.push(i[_this.valueField]);
12874                 return;
12875             }
12876
12877             value.push(i);
12878         });
12879
12880         this.value = value.join(',');
12881
12882         if(this.hiddenField){
12883             this.hiddenField.dom.value = this.value;
12884         }
12885         
12886         this.store.fireEvent("datachanged", this.store);
12887     },
12888     
12889     clearItem : function()
12890     {
12891         if(!this.multiple){
12892             return;
12893         }
12894         
12895         this.item = [];
12896         
12897         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12898            c.remove();
12899         });
12900         
12901         this.syncValue();
12902         
12903         this.validate();
12904     },
12905     
12906     inputEl: function ()
12907     {
12908         if(Roo.isTouch && this.mobileTouchView){
12909             return this.el.select('input.form-control',true).first();
12910         }
12911         
12912         if(this.tickable){
12913             return this.searchField;
12914         }
12915         
12916         return this.el.select('input.form-control',true).first();
12917     },
12918     
12919     
12920     onTickableFooterButtonClick : function(e, btn, el)
12921     {
12922         e.preventDefault();
12923         
12924         this.lastItem = Roo.apply([], this.item);
12925         
12926         if(btn && btn.name == 'cancel'){
12927             this.tickItems = Roo.apply([], this.item);
12928             this.collapse();
12929             return;
12930         }
12931         
12932         this.clearItem();
12933         
12934         var _this = this;
12935         
12936         Roo.each(this.tickItems, function(o){
12937             _this.addItem(o);
12938         });
12939         
12940         this.collapse();
12941         
12942     },
12943     
12944     validate : function()
12945     {
12946         var v = this.getRawValue();
12947         
12948         if(this.multiple){
12949             v = this.getValue();
12950         }
12951         
12952         if(this.disabled || this.allowBlank || v.length){
12953             this.markValid();
12954             return true;
12955         }
12956         
12957         this.markInvalid();
12958         return false;
12959     },
12960     
12961     tickableInputEl : function()
12962     {
12963         if(!this.tickable || !this.editable){
12964             return this.inputEl();
12965         }
12966         
12967         return this.inputEl().select('.select2-search-field-input', true).first();
12968     },
12969     
12970     
12971     getAutoCreateTouchView : function()
12972     {
12973         var id = Roo.id();
12974         
12975         var cfg = {
12976             cls: 'form-group' //input-group
12977         };
12978         
12979         var input =  {
12980             tag: 'input',
12981             id : id,
12982             type : this.inputType,
12983             cls : 'form-control x-combo-noedit',
12984             autocomplete: 'new-password',
12985             placeholder : this.placeholder || '',
12986             readonly : true
12987         };
12988         
12989         if (this.name) {
12990             input.name = this.name;
12991         }
12992         
12993         if (this.size) {
12994             input.cls += ' input-' + this.size;
12995         }
12996         
12997         if (this.disabled) {
12998             input.disabled = true;
12999         }
13000         
13001         var inputblock = {
13002             cls : '',
13003             cn : [
13004                 input
13005             ]
13006         };
13007         
13008         if(this.before){
13009             inputblock.cls += ' input-group';
13010             
13011             inputblock.cn.unshift({
13012                 tag :'span',
13013                 cls : 'input-group-addon',
13014                 html : this.before
13015             });
13016         }
13017         
13018         if(this.removable && !this.multiple){
13019             inputblock.cls += ' roo-removable';
13020             
13021             inputblock.cn.push({
13022                 tag: 'button',
13023                 html : 'x',
13024                 cls : 'roo-combo-removable-btn close'
13025             });
13026         }
13027
13028         if(this.hasFeedback && !this.allowBlank){
13029             
13030             inputblock.cls += ' has-feedback';
13031             
13032             inputblock.cn.push({
13033                 tag: 'span',
13034                 cls: 'glyphicon form-control-feedback'
13035             });
13036             
13037         }
13038         
13039         if (this.after) {
13040             
13041             inputblock.cls += (this.before) ? '' : ' input-group';
13042             
13043             inputblock.cn.push({
13044                 tag :'span',
13045                 cls : 'input-group-addon',
13046                 html : this.after
13047             });
13048         }
13049
13050         var box = {
13051             tag: 'div',
13052             cn: [
13053                 {
13054                     tag: 'input',
13055                     type : 'hidden',
13056                     cls: 'form-hidden-field'
13057                 },
13058                 inputblock
13059             ]
13060             
13061         };
13062         
13063         if(this.multiple){
13064             box = {
13065                 tag: 'div',
13066                 cn: [
13067                     {
13068                         tag: 'input',
13069                         type : 'hidden',
13070                         cls: 'form-hidden-field'
13071                     },
13072                     {
13073                         tag: 'ul',
13074                         cls: 'select2-choices',
13075                         cn:[
13076                             {
13077                                 tag: 'li',
13078                                 cls: 'select2-search-field',
13079                                 cn: [
13080
13081                                     inputblock
13082                                 ]
13083                             }
13084                         ]
13085                     }
13086                 ]
13087             }
13088         };
13089         
13090         var combobox = {
13091             cls: 'select2-container input-group',
13092             cn: [
13093                 box
13094             ]
13095         };
13096         
13097         if(this.multiple){
13098             combobox.cls += ' select2-container-multi';
13099         }
13100         
13101         var align = this.labelAlign || this.parentLabelAlign();
13102         
13103         cfg.cn = combobox;
13104         
13105         if(this.fieldLabel.length){
13106             
13107             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13108             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13109             
13110             cfg.cn = [
13111                 {
13112                     tag: 'label',
13113                     cls : 'control-label ' + lw,
13114                     html : this.fieldLabel
13115
13116                 },
13117                 {
13118                     cls : cw, 
13119                     cn: [
13120                         combobox
13121                     ]
13122                 }
13123             ];
13124         }
13125         
13126         var settings = this;
13127         
13128         ['xs','sm','md','lg'].map(function(size){
13129             if (settings[size]) {
13130                 cfg.cls += ' col-' + size + '-' + settings[size];
13131             }
13132         });
13133         
13134         return cfg;
13135     },
13136     
13137     initTouchView : function()
13138     {
13139         this.renderTouchView();
13140         
13141         this.touchViewEl.on('scroll', function(){
13142             this.el.dom.scrollTop = 0;
13143         }, this);
13144         
13145         this.inputEl().on("click", this.showTouchView, this);
13146         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13147         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13148         
13149         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13150         
13151         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13152         this.store.on('load', this.onTouchViewLoad, this);
13153         this.store.on('loadexception', this.onTouchViewLoadException, this);
13154         
13155         if(this.hiddenName){
13156             
13157             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13158             
13159             this.hiddenField.dom.value =
13160                 this.hiddenValue !== undefined ? this.hiddenValue :
13161                 this.value !== undefined ? this.value : '';
13162         
13163             this.el.dom.removeAttribute('name');
13164             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13165         }
13166         
13167         if(this.multiple){
13168             this.choices = this.el.select('ul.select2-choices', true).first();
13169             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13170         }
13171         
13172         if(this.removable && !this.multiple){
13173             var close = this.closeTriggerEl();
13174             if(close){
13175                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13176                 close.on('click', this.removeBtnClick, this, close);
13177             }
13178         }
13179         
13180         return;
13181         
13182         
13183     },
13184     
13185     renderTouchView : function()
13186     {
13187         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13188         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13189         
13190         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13191         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13192         
13193         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13194         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13195         this.touchViewBodyEl.setStyle('overflow', 'auto');
13196         
13197         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13198         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13199         
13200         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13201         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13202         
13203     },
13204     
13205     showTouchView : function()
13206     {
13207         this.touchViewHeaderEl.hide();
13208
13209         if(this.fieldLabel.length){
13210             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13211             this.touchViewHeaderEl.show();
13212         }
13213
13214         this.touchViewEl.show();
13215
13216         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13217         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13218
13219         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13220
13221         if(this.fieldLabel.length){
13222             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13223         }
13224         
13225         this.touchViewBodyEl.setHeight(bodyHeight);
13226
13227         if(this.animate){
13228             var _this = this;
13229             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13230         }else{
13231             this.touchViewEl.addClass('in');
13232         }
13233
13234         this.doTouchViewQuery();
13235         
13236     },
13237     
13238     hideTouchView : function()
13239     {
13240         this.touchViewEl.removeClass('in');
13241
13242         if(this.animate){
13243             var _this = this;
13244             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13245         }else{
13246             this.touchViewEl.setStyle('display', 'none');
13247         }
13248         
13249     },
13250     
13251     setTouchViewValue : function()
13252     {
13253         if(this.multiple){
13254             this.clearItem();
13255         
13256             var _this = this;
13257
13258             Roo.each(this.tickItems, function(o){
13259                 this.addItem(o);
13260             }, this);
13261         }
13262         
13263         this.hideTouchView();
13264     },
13265     
13266     doTouchViewQuery : function()
13267     {
13268         var qe = {
13269             query: '',
13270             forceAll: true,
13271             combo: this,
13272             cancel:false
13273         };
13274         
13275         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13276             return false;
13277         }
13278         
13279         if(!this.alwaysQuery || this.mode == 'local'){
13280             this.onTouchViewLoad();
13281             return;
13282         }
13283         
13284         this.store.load();
13285     },
13286     
13287     onTouchViewBeforeLoad : function(combo,opts)
13288     {
13289         return;
13290     },
13291
13292     // private
13293     onTouchViewLoad : function()
13294     {
13295         if(this.store.getCount() < 1){
13296             this.onTouchViewEmptyResults();
13297             return;
13298         }
13299         
13300         this.clearTouchView();
13301         
13302         var rawValue = this.getRawValue();
13303         
13304         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13305         
13306         this.tickItems = [];
13307         
13308         this.store.data.each(function(d, rowIndex){
13309             var row = this.touchViewListGroup.createChild(template);
13310             
13311             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13312                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13313             }
13314             
13315             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13316                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13317             }
13318             
13319             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13320                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13321                 this.tickItems.push(d.data);
13322             }
13323             
13324             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13325             
13326         }, this);
13327         
13328         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13329         
13330         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13331
13332         if(this.fieldLabel.length){
13333             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13334         }
13335
13336         var listHeight = this.touchViewListGroup.getHeight();
13337         
13338         if(firstChecked && listHeight > bodyHeight){
13339             (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13340         }
13341         
13342     },
13343     
13344     onTouchViewLoadException : function()
13345     {
13346         this.hideTouchView();
13347     },
13348     
13349     onTouchViewEmptyResults : function()
13350     {
13351         this.clearTouchView();
13352         
13353         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13354         
13355         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13356         
13357     },
13358     
13359     clearTouchView : function()
13360     {
13361         this.touchViewListGroup.dom.innerHTML = '';
13362     },
13363     
13364     onTouchViewClick : function(e, el, o)
13365     {
13366         e.preventDefault();
13367         
13368         var row = o.row;
13369         var rowIndex = o.rowIndex;
13370         
13371         var r = this.store.getAt(rowIndex);
13372         
13373         if(!this.multiple){
13374             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13375                 c.dom.removeAttribute('checked');
13376             }, this);
13377             
13378             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13379         
13380             this.setFromData(r.data);
13381             
13382             var close = this.closeTriggerEl();
13383         
13384             if(close){
13385                 close.show();
13386             }
13387
13388             this.hideTouchView();
13389             
13390             this.fireEvent('select', this, r, rowIndex);
13391             
13392             return;
13393         }
13394         
13395         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13396             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13397             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13398             return;
13399         }
13400         
13401         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13402         this.addItem(r.data);
13403         this.tickItems.push(r.data);
13404         
13405     }
13406     
13407
13408     /** 
13409     * @cfg {Boolean} grow 
13410     * @hide 
13411     */
13412     /** 
13413     * @cfg {Number} growMin 
13414     * @hide 
13415     */
13416     /** 
13417     * @cfg {Number} growMax 
13418     * @hide 
13419     */
13420     /**
13421      * @hide
13422      * @method autoSize
13423      */
13424 });
13425
13426 Roo.apply(Roo.bootstrap.ComboBox,  {
13427     
13428     header : {
13429         tag: 'div',
13430         cls: 'modal-header',
13431         cn: [
13432             {
13433                 tag: 'h4',
13434                 cls: 'modal-title'
13435             }
13436         ]
13437     },
13438     
13439     body : {
13440         tag: 'div',
13441         cls: 'modal-body',
13442         cn: [
13443             {
13444                 tag: 'ul',
13445                 cls: 'list-group'
13446             }
13447         ]
13448     },
13449     
13450     listItemRadio : {
13451         tag: 'li',
13452         cls: 'list-group-item',
13453         cn: [
13454             {
13455                 tag: 'span',
13456                 cls: 'roo-combobox-list-group-item-value'
13457             },
13458             {
13459                 tag: 'div',
13460                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13461                 cn: [
13462                     {
13463                         tag: 'input',
13464                         type: 'radio'
13465                     },
13466                     {
13467                         tag: 'label'
13468                     }
13469                 ]
13470             }
13471         ]
13472     },
13473     
13474     listItemCheckbox : {
13475         tag: 'li',
13476         cls: 'list-group-item',
13477         cn: [
13478             {
13479                 tag: 'span',
13480                 cls: 'roo-combobox-list-group-item-value'
13481             },
13482             {
13483                 tag: 'div',
13484                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13485                 cn: [
13486                     {
13487                         tag: 'input',
13488                         type: 'checkbox'
13489                     },
13490                     {
13491                         tag: 'label'
13492                     }
13493                 ]
13494             }
13495         ]
13496     },
13497     
13498     emptyResult : {
13499         tag: 'div',
13500         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13501     },
13502     
13503     footer : {
13504         tag: 'div',
13505         cls: 'modal-footer',
13506         cn: [
13507             {
13508                 tag: 'div',
13509                 cls: 'row',
13510                 cn: [
13511                     {
13512                         tag: 'div',
13513                         cls: 'col-xs-6 text-left',
13514                         cn: {
13515                             tag: 'button',
13516                             cls: 'btn btn-danger roo-touch-view-cancel',
13517                             html: 'Cancel'
13518                         }
13519                     },
13520                     {
13521                         tag: 'div',
13522                         cls: 'col-xs-6 text-right',
13523                         cn: {
13524                             tag: 'button',
13525                             cls: 'btn btn-success roo-touch-view-ok',
13526                             html: 'OK'
13527                         }
13528                     }
13529                 ]
13530             }
13531         ]
13532         
13533     }
13534 });
13535
13536 Roo.apply(Roo.bootstrap.ComboBox,  {
13537     
13538     touchViewTemplate : {
13539         tag: 'div',
13540         cls: 'modal fade roo-combobox-touch-view',
13541         cn: [
13542             {
13543                 tag: 'div',
13544                 cls: 'modal-dialog',
13545                 cn: [
13546                     {
13547                         tag: 'div',
13548                         cls: 'modal-content',
13549                         cn: [
13550                             Roo.bootstrap.ComboBox.header,
13551                             Roo.bootstrap.ComboBox.body,
13552                             Roo.bootstrap.ComboBox.footer
13553                         ]
13554                     }
13555                 ]
13556             }
13557         ]
13558     }
13559 });/*
13560  * Based on:
13561  * Ext JS Library 1.1.1
13562  * Copyright(c) 2006-2007, Ext JS, LLC.
13563  *
13564  * Originally Released Under LGPL - original licence link has changed is not relivant.
13565  *
13566  * Fork - LGPL
13567  * <script type="text/javascript">
13568  */
13569
13570 /**
13571  * @class Roo.View
13572  * @extends Roo.util.Observable
13573  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13574  * This class also supports single and multi selection modes. <br>
13575  * Create a data model bound view:
13576  <pre><code>
13577  var store = new Roo.data.Store(...);
13578
13579  var view = new Roo.View({
13580     el : "my-element",
13581     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13582  
13583     singleSelect: true,
13584     selectedClass: "ydataview-selected",
13585     store: store
13586  });
13587
13588  // listen for node click?
13589  view.on("click", function(vw, index, node, e){
13590  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13591  });
13592
13593  // load XML data
13594  dataModel.load("foobar.xml");
13595  </code></pre>
13596  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13597  * <br><br>
13598  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13599  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13600  * 
13601  * Note: old style constructor is still suported (container, template, config)
13602  * 
13603  * @constructor
13604  * Create a new View
13605  * @param {Object} config The config object
13606  * 
13607  */
13608 Roo.View = function(config, depreciated_tpl, depreciated_config){
13609     
13610     this.parent = false;
13611     
13612     if (typeof(depreciated_tpl) == 'undefined') {
13613         // new way.. - universal constructor.
13614         Roo.apply(this, config);
13615         this.el  = Roo.get(this.el);
13616     } else {
13617         // old format..
13618         this.el  = Roo.get(config);
13619         this.tpl = depreciated_tpl;
13620         Roo.apply(this, depreciated_config);
13621     }
13622     this.wrapEl  = this.el.wrap().wrap();
13623     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13624     
13625     
13626     if(typeof(this.tpl) == "string"){
13627         this.tpl = new Roo.Template(this.tpl);
13628     } else {
13629         // support xtype ctors..
13630         this.tpl = new Roo.factory(this.tpl, Roo);
13631     }
13632     
13633     
13634     this.tpl.compile();
13635     
13636     /** @private */
13637     this.addEvents({
13638         /**
13639          * @event beforeclick
13640          * Fires before a click is processed. Returns false to cancel the default action.
13641          * @param {Roo.View} this
13642          * @param {Number} index The index of the target node
13643          * @param {HTMLElement} node The target node
13644          * @param {Roo.EventObject} e The raw event object
13645          */
13646             "beforeclick" : true,
13647         /**
13648          * @event click
13649          * Fires when a template node is clicked.
13650          * @param {Roo.View} this
13651          * @param {Number} index The index of the target node
13652          * @param {HTMLElement} node The target node
13653          * @param {Roo.EventObject} e The raw event object
13654          */
13655             "click" : true,
13656         /**
13657          * @event dblclick
13658          * Fires when a template node is double clicked.
13659          * @param {Roo.View} this
13660          * @param {Number} index The index of the target node
13661          * @param {HTMLElement} node The target node
13662          * @param {Roo.EventObject} e The raw event object
13663          */
13664             "dblclick" : true,
13665         /**
13666          * @event contextmenu
13667          * Fires when a template node is right clicked.
13668          * @param {Roo.View} this
13669          * @param {Number} index The index of the target node
13670          * @param {HTMLElement} node The target node
13671          * @param {Roo.EventObject} e The raw event object
13672          */
13673             "contextmenu" : true,
13674         /**
13675          * @event selectionchange
13676          * Fires when the selected nodes change.
13677          * @param {Roo.View} this
13678          * @param {Array} selections Array of the selected nodes
13679          */
13680             "selectionchange" : true,
13681     
13682         /**
13683          * @event beforeselect
13684          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13685          * @param {Roo.View} this
13686          * @param {HTMLElement} node The node to be selected
13687          * @param {Array} selections Array of currently selected nodes
13688          */
13689             "beforeselect" : true,
13690         /**
13691          * @event preparedata
13692          * Fires on every row to render, to allow you to change the data.
13693          * @param {Roo.View} this
13694          * @param {Object} data to be rendered (change this)
13695          */
13696           "preparedata" : true
13697           
13698           
13699         });
13700
13701
13702
13703     this.el.on({
13704         "click": this.onClick,
13705         "dblclick": this.onDblClick,
13706         "contextmenu": this.onContextMenu,
13707         scope:this
13708     });
13709
13710     this.selections = [];
13711     this.nodes = [];
13712     this.cmp = new Roo.CompositeElementLite([]);
13713     if(this.store){
13714         this.store = Roo.factory(this.store, Roo.data);
13715         this.setStore(this.store, true);
13716     }
13717     
13718     if ( this.footer && this.footer.xtype) {
13719            
13720          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13721         
13722         this.footer.dataSource = this.store
13723         this.footer.container = fctr;
13724         this.footer = Roo.factory(this.footer, Roo);
13725         fctr.insertFirst(this.el);
13726         
13727         // this is a bit insane - as the paging toolbar seems to detach the el..
13728 //        dom.parentNode.parentNode.parentNode
13729          // they get detached?
13730     }
13731     
13732     
13733     Roo.View.superclass.constructor.call(this);
13734     
13735     
13736 };
13737
13738 Roo.extend(Roo.View, Roo.util.Observable, {
13739     
13740      /**
13741      * @cfg {Roo.data.Store} store Data store to load data from.
13742      */
13743     store : false,
13744     
13745     /**
13746      * @cfg {String|Roo.Element} el The container element.
13747      */
13748     el : '',
13749     
13750     /**
13751      * @cfg {String|Roo.Template} tpl The template used by this View 
13752      */
13753     tpl : false,
13754     /**
13755      * @cfg {String} dataName the named area of the template to use as the data area
13756      *                          Works with domtemplates roo-name="name"
13757      */
13758     dataName: false,
13759     /**
13760      * @cfg {String} selectedClass The css class to add to selected nodes
13761      */
13762     selectedClass : "x-view-selected",
13763      /**
13764      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13765      */
13766     emptyText : "",
13767     
13768     /**
13769      * @cfg {String} text to display on mask (default Loading)
13770      */
13771     mask : false,
13772     /**
13773      * @cfg {Boolean} multiSelect Allow multiple selection
13774      */
13775     multiSelect : false,
13776     /**
13777      * @cfg {Boolean} singleSelect Allow single selection
13778      */
13779     singleSelect:  false,
13780     
13781     /**
13782      * @cfg {Boolean} toggleSelect - selecting 
13783      */
13784     toggleSelect : false,
13785     
13786     /**
13787      * @cfg {Boolean} tickable - selecting 
13788      */
13789     tickable : false,
13790     
13791     /**
13792      * Returns the element this view is bound to.
13793      * @return {Roo.Element}
13794      */
13795     getEl : function(){
13796         return this.wrapEl;
13797     },
13798     
13799     
13800
13801     /**
13802      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13803      */
13804     refresh : function(){
13805         //Roo.log('refresh');
13806         var t = this.tpl;
13807         
13808         // if we are using something like 'domtemplate', then
13809         // the what gets used is:
13810         // t.applySubtemplate(NAME, data, wrapping data..)
13811         // the outer template then get' applied with
13812         //     the store 'extra data'
13813         // and the body get's added to the
13814         //      roo-name="data" node?
13815         //      <span class='roo-tpl-{name}'></span> ?????
13816         
13817         
13818         
13819         this.clearSelections();
13820         this.el.update("");
13821         var html = [];
13822         var records = this.store.getRange();
13823         if(records.length < 1) {
13824             
13825             // is this valid??  = should it render a template??
13826             
13827             this.el.update(this.emptyText);
13828             return;
13829         }
13830         var el = this.el;
13831         if (this.dataName) {
13832             this.el.update(t.apply(this.store.meta)); //????
13833             el = this.el.child('.roo-tpl-' + this.dataName);
13834         }
13835         
13836         for(var i = 0, len = records.length; i < len; i++){
13837             var data = this.prepareData(records[i].data, i, records[i]);
13838             this.fireEvent("preparedata", this, data, i, records[i]);
13839             
13840             var d = Roo.apply({}, data);
13841             
13842             if(this.tickable){
13843                 Roo.apply(d, {'roo-id' : Roo.id()});
13844                 
13845                 var _this = this;
13846             
13847                 Roo.each(this.parent.item, function(item){
13848                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13849                         return;
13850                     }
13851                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13852                 });
13853             }
13854             
13855             html[html.length] = Roo.util.Format.trim(
13856                 this.dataName ?
13857                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13858                     t.apply(d)
13859             );
13860         }
13861         
13862         
13863         
13864         el.update(html.join(""));
13865         this.nodes = el.dom.childNodes;
13866         this.updateIndexes(0);
13867     },
13868     
13869
13870     /**
13871      * Function to override to reformat the data that is sent to
13872      * the template for each node.
13873      * DEPRICATED - use the preparedata event handler.
13874      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13875      * a JSON object for an UpdateManager bound view).
13876      */
13877     prepareData : function(data, index, record)
13878     {
13879         this.fireEvent("preparedata", this, data, index, record);
13880         return data;
13881     },
13882
13883     onUpdate : function(ds, record){
13884         // Roo.log('on update');   
13885         this.clearSelections();
13886         var index = this.store.indexOf(record);
13887         var n = this.nodes[index];
13888         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13889         n.parentNode.removeChild(n);
13890         this.updateIndexes(index, index);
13891     },
13892
13893     
13894     
13895 // --------- FIXME     
13896     onAdd : function(ds, records, index)
13897     {
13898         //Roo.log(['on Add', ds, records, index] );        
13899         this.clearSelections();
13900         if(this.nodes.length == 0){
13901             this.refresh();
13902             return;
13903         }
13904         var n = this.nodes[index];
13905         for(var i = 0, len = records.length; i < len; i++){
13906             var d = this.prepareData(records[i].data, i, records[i]);
13907             if(n){
13908                 this.tpl.insertBefore(n, d);
13909             }else{
13910                 
13911                 this.tpl.append(this.el, d);
13912             }
13913         }
13914         this.updateIndexes(index);
13915     },
13916
13917     onRemove : function(ds, record, index){
13918        // Roo.log('onRemove');
13919         this.clearSelections();
13920         var el = this.dataName  ?
13921             this.el.child('.roo-tpl-' + this.dataName) :
13922             this.el; 
13923         
13924         el.dom.removeChild(this.nodes[index]);
13925         this.updateIndexes(index);
13926     },
13927
13928     /**
13929      * Refresh an individual node.
13930      * @param {Number} index
13931      */
13932     refreshNode : function(index){
13933         this.onUpdate(this.store, this.store.getAt(index));
13934     },
13935
13936     updateIndexes : function(startIndex, endIndex){
13937         var ns = this.nodes;
13938         startIndex = startIndex || 0;
13939         endIndex = endIndex || ns.length - 1;
13940         for(var i = startIndex; i <= endIndex; i++){
13941             ns[i].nodeIndex = i;
13942         }
13943     },
13944
13945     /**
13946      * Changes the data store this view uses and refresh the view.
13947      * @param {Store} store
13948      */
13949     setStore : function(store, initial){
13950         if(!initial && this.store){
13951             this.store.un("datachanged", this.refresh);
13952             this.store.un("add", this.onAdd);
13953             this.store.un("remove", this.onRemove);
13954             this.store.un("update", this.onUpdate);
13955             this.store.un("clear", this.refresh);
13956             this.store.un("beforeload", this.onBeforeLoad);
13957             this.store.un("load", this.onLoad);
13958             this.store.un("loadexception", this.onLoad);
13959         }
13960         if(store){
13961           
13962             store.on("datachanged", this.refresh, this);
13963             store.on("add", this.onAdd, this);
13964             store.on("remove", this.onRemove, this);
13965             store.on("update", this.onUpdate, this);
13966             store.on("clear", this.refresh, this);
13967             store.on("beforeload", this.onBeforeLoad, this);
13968             store.on("load", this.onLoad, this);
13969             store.on("loadexception", this.onLoad, this);
13970         }
13971         
13972         if(store){
13973             this.refresh();
13974         }
13975     },
13976     /**
13977      * onbeforeLoad - masks the loading area.
13978      *
13979      */
13980     onBeforeLoad : function(store,opts)
13981     {
13982          //Roo.log('onBeforeLoad');   
13983         if (!opts.add) {
13984             this.el.update("");
13985         }
13986         this.el.mask(this.mask ? this.mask : "Loading" ); 
13987     },
13988     onLoad : function ()
13989     {
13990         this.el.unmask();
13991     },
13992     
13993
13994     /**
13995      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13996      * @param {HTMLElement} node
13997      * @return {HTMLElement} The template node
13998      */
13999     findItemFromChild : function(node){
14000         var el = this.dataName  ?
14001             this.el.child('.roo-tpl-' + this.dataName,true) :
14002             this.el.dom; 
14003         
14004         if(!node || node.parentNode == el){
14005                     return node;
14006             }
14007             var p = node.parentNode;
14008             while(p && p != el){
14009             if(p.parentNode == el){
14010                 return p;
14011             }
14012             p = p.parentNode;
14013         }
14014             return null;
14015     },
14016
14017     /** @ignore */
14018     onClick : function(e){
14019         var item = this.findItemFromChild(e.getTarget());
14020         if(item){
14021             var index = this.indexOf(item);
14022             if(this.onItemClick(item, index, e) !== false){
14023                 this.fireEvent("click", this, index, item, e);
14024             }
14025         }else{
14026             this.clearSelections();
14027         }
14028     },
14029
14030     /** @ignore */
14031     onContextMenu : function(e){
14032         var item = this.findItemFromChild(e.getTarget());
14033         if(item){
14034             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14035         }
14036     },
14037
14038     /** @ignore */
14039     onDblClick : function(e){
14040         var item = this.findItemFromChild(e.getTarget());
14041         if(item){
14042             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14043         }
14044     },
14045
14046     onItemClick : function(item, index, e)
14047     {
14048         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14049             return false;
14050         }
14051         if (this.toggleSelect) {
14052             var m = this.isSelected(item) ? 'unselect' : 'select';
14053             //Roo.log(m);
14054             var _t = this;
14055             _t[m](item, true, false);
14056             return true;
14057         }
14058         if(this.multiSelect || this.singleSelect){
14059             if(this.multiSelect && e.shiftKey && this.lastSelection){
14060                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14061             }else{
14062                 this.select(item, this.multiSelect && e.ctrlKey);
14063                 this.lastSelection = item;
14064             }
14065             
14066             if(!this.tickable){
14067                 e.preventDefault();
14068             }
14069             
14070         }
14071         return true;
14072     },
14073
14074     /**
14075      * Get the number of selected nodes.
14076      * @return {Number}
14077      */
14078     getSelectionCount : function(){
14079         return this.selections.length;
14080     },
14081
14082     /**
14083      * Get the currently selected nodes.
14084      * @return {Array} An array of HTMLElements
14085      */
14086     getSelectedNodes : function(){
14087         return this.selections;
14088     },
14089
14090     /**
14091      * Get the indexes of the selected nodes.
14092      * @return {Array}
14093      */
14094     getSelectedIndexes : function(){
14095         var indexes = [], s = this.selections;
14096         for(var i = 0, len = s.length; i < len; i++){
14097             indexes.push(s[i].nodeIndex);
14098         }
14099         return indexes;
14100     },
14101
14102     /**
14103      * Clear all selections
14104      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14105      */
14106     clearSelections : function(suppressEvent){
14107         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14108             this.cmp.elements = this.selections;
14109             this.cmp.removeClass(this.selectedClass);
14110             this.selections = [];
14111             if(!suppressEvent){
14112                 this.fireEvent("selectionchange", this, this.selections);
14113             }
14114         }
14115     },
14116
14117     /**
14118      * Returns true if the passed node is selected
14119      * @param {HTMLElement/Number} node The node or node index
14120      * @return {Boolean}
14121      */
14122     isSelected : function(node){
14123         var s = this.selections;
14124         if(s.length < 1){
14125             return false;
14126         }
14127         node = this.getNode(node);
14128         return s.indexOf(node) !== -1;
14129     },
14130
14131     /**
14132      * Selects nodes.
14133      * @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
14134      * @param {Boolean} keepExisting (optional) true to keep existing selections
14135      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14136      */
14137     select : function(nodeInfo, keepExisting, suppressEvent){
14138         if(nodeInfo instanceof Array){
14139             if(!keepExisting){
14140                 this.clearSelections(true);
14141             }
14142             for(var i = 0, len = nodeInfo.length; i < len; i++){
14143                 this.select(nodeInfo[i], true, true);
14144             }
14145             return;
14146         } 
14147         var node = this.getNode(nodeInfo);
14148         if(!node || this.isSelected(node)){
14149             return; // already selected.
14150         }
14151         if(!keepExisting){
14152             this.clearSelections(true);
14153         }
14154         
14155         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14156             Roo.fly(node).addClass(this.selectedClass);
14157             this.selections.push(node);
14158             if(!suppressEvent){
14159                 this.fireEvent("selectionchange", this, this.selections);
14160             }
14161         }
14162         
14163         
14164     },
14165       /**
14166      * Unselects nodes.
14167      * @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
14168      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14169      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14170      */
14171     unselect : function(nodeInfo, keepExisting, suppressEvent)
14172     {
14173         if(nodeInfo instanceof Array){
14174             Roo.each(this.selections, function(s) {
14175                 this.unselect(s, nodeInfo);
14176             }, this);
14177             return;
14178         }
14179         var node = this.getNode(nodeInfo);
14180         if(!node || !this.isSelected(node)){
14181             //Roo.log("not selected");
14182             return; // not selected.
14183         }
14184         // fireevent???
14185         var ns = [];
14186         Roo.each(this.selections, function(s) {
14187             if (s == node ) {
14188                 Roo.fly(node).removeClass(this.selectedClass);
14189
14190                 return;
14191             }
14192             ns.push(s);
14193         },this);
14194         
14195         this.selections= ns;
14196         this.fireEvent("selectionchange", this, this.selections);
14197     },
14198
14199     /**
14200      * Gets a template node.
14201      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14202      * @return {HTMLElement} The node or null if it wasn't found
14203      */
14204     getNode : function(nodeInfo){
14205         if(typeof nodeInfo == "string"){
14206             return document.getElementById(nodeInfo);
14207         }else if(typeof nodeInfo == "number"){
14208             return this.nodes[nodeInfo];
14209         }
14210         return nodeInfo;
14211     },
14212
14213     /**
14214      * Gets a range template nodes.
14215      * @param {Number} startIndex
14216      * @param {Number} endIndex
14217      * @return {Array} An array of nodes
14218      */
14219     getNodes : function(start, end){
14220         var ns = this.nodes;
14221         start = start || 0;
14222         end = typeof end == "undefined" ? ns.length - 1 : end;
14223         var nodes = [];
14224         if(start <= end){
14225             for(var i = start; i <= end; i++){
14226                 nodes.push(ns[i]);
14227             }
14228         } else{
14229             for(var i = start; i >= end; i--){
14230                 nodes.push(ns[i]);
14231             }
14232         }
14233         return nodes;
14234     },
14235
14236     /**
14237      * Finds the index of the passed node
14238      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14239      * @return {Number} The index of the node or -1
14240      */
14241     indexOf : function(node){
14242         node = this.getNode(node);
14243         if(typeof node.nodeIndex == "number"){
14244             return node.nodeIndex;
14245         }
14246         var ns = this.nodes;
14247         for(var i = 0, len = ns.length; i < len; i++){
14248             if(ns[i] == node){
14249                 return i;
14250             }
14251         }
14252         return -1;
14253     }
14254 });
14255 /*
14256  * - LGPL
14257  *
14258  * based on jquery fullcalendar
14259  * 
14260  */
14261
14262 Roo.bootstrap = Roo.bootstrap || {};
14263 /**
14264  * @class Roo.bootstrap.Calendar
14265  * @extends Roo.bootstrap.Component
14266  * Bootstrap Calendar class
14267  * @cfg {Boolean} loadMask (true|false) default false
14268  * @cfg {Object} header generate the user specific header of the calendar, default false
14269
14270  * @constructor
14271  * Create a new Container
14272  * @param {Object} config The config object
14273  */
14274
14275
14276
14277 Roo.bootstrap.Calendar = function(config){
14278     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14279      this.addEvents({
14280         /**
14281              * @event select
14282              * Fires when a date is selected
14283              * @param {DatePicker} this
14284              * @param {Date} date The selected date
14285              */
14286         'select': true,
14287         /**
14288              * @event monthchange
14289              * Fires when the displayed month changes 
14290              * @param {DatePicker} this
14291              * @param {Date} date The selected month
14292              */
14293         'monthchange': true,
14294         /**
14295              * @event evententer
14296              * Fires when mouse over an event
14297              * @param {Calendar} this
14298              * @param {event} Event
14299              */
14300         'evententer': true,
14301         /**
14302              * @event eventleave
14303              * Fires when the mouse leaves an
14304              * @param {Calendar} this
14305              * @param {event}
14306              */
14307         'eventleave': true,
14308         /**
14309              * @event eventclick
14310              * Fires when the mouse click an
14311              * @param {Calendar} this
14312              * @param {event}
14313              */
14314         'eventclick': true
14315         
14316     });
14317
14318 };
14319
14320 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14321     
14322      /**
14323      * @cfg {Number} startDay
14324      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14325      */
14326     startDay : 0,
14327     
14328     loadMask : false,
14329     
14330     header : false,
14331       
14332     getAutoCreate : function(){
14333         
14334         
14335         var fc_button = function(name, corner, style, content ) {
14336             return Roo.apply({},{
14337                 tag : 'span',
14338                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14339                          (corner.length ?
14340                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14341                             ''
14342                         ),
14343                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14344                 unselectable: 'on'
14345             });
14346         };
14347         
14348         var header = {};
14349         
14350         if(!this.header){
14351             header = {
14352                 tag : 'table',
14353                 cls : 'fc-header',
14354                 style : 'width:100%',
14355                 cn : [
14356                     {
14357                         tag: 'tr',
14358                         cn : [
14359                             {
14360                                 tag : 'td',
14361                                 cls : 'fc-header-left',
14362                                 cn : [
14363                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14364                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14365                                     { tag: 'span', cls: 'fc-header-space' },
14366                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14367
14368
14369                                 ]
14370                             },
14371
14372                             {
14373                                 tag : 'td',
14374                                 cls : 'fc-header-center',
14375                                 cn : [
14376                                     {
14377                                         tag: 'span',
14378                                         cls: 'fc-header-title',
14379                                         cn : {
14380                                             tag: 'H2',
14381                                             html : 'month / year'
14382                                         }
14383                                     }
14384
14385                                 ]
14386                             },
14387                             {
14388                                 tag : 'td',
14389                                 cls : 'fc-header-right',
14390                                 cn : [
14391                               /*      fc_button('month', 'left', '', 'month' ),
14392                                     fc_button('week', '', '', 'week' ),
14393                                     fc_button('day', 'right', '', 'day' )
14394                                 */    
14395
14396                                 ]
14397                             }
14398
14399                         ]
14400                     }
14401                 ]
14402             };
14403         }
14404         
14405         header = this.header;
14406         
14407        
14408         var cal_heads = function() {
14409             var ret = [];
14410             // fixme - handle this.
14411             
14412             for (var i =0; i < Date.dayNames.length; i++) {
14413                 var d = Date.dayNames[i];
14414                 ret.push({
14415                     tag: 'th',
14416                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14417                     html : d.substring(0,3)
14418                 });
14419                 
14420             }
14421             ret[0].cls += ' fc-first';
14422             ret[6].cls += ' fc-last';
14423             return ret;
14424         };
14425         var cal_cell = function(n) {
14426             return  {
14427                 tag: 'td',
14428                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14429                 cn : [
14430                     {
14431                         cn : [
14432                             {
14433                                 cls: 'fc-day-number',
14434                                 html: 'D'
14435                             },
14436                             {
14437                                 cls: 'fc-day-content',
14438                              
14439                                 cn : [
14440                                      {
14441                                         style: 'position: relative;' // height: 17px;
14442                                     }
14443                                 ]
14444                             }
14445                             
14446                             
14447                         ]
14448                     }
14449                 ]
14450                 
14451             }
14452         };
14453         var cal_rows = function() {
14454             
14455             var ret = [];
14456             for (var r = 0; r < 6; r++) {
14457                 var row= {
14458                     tag : 'tr',
14459                     cls : 'fc-week',
14460                     cn : []
14461                 };
14462                 
14463                 for (var i =0; i < Date.dayNames.length; i++) {
14464                     var d = Date.dayNames[i];
14465                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14466
14467                 }
14468                 row.cn[0].cls+=' fc-first';
14469                 row.cn[0].cn[0].style = 'min-height:90px';
14470                 row.cn[6].cls+=' fc-last';
14471                 ret.push(row);
14472                 
14473             }
14474             ret[0].cls += ' fc-first';
14475             ret[4].cls += ' fc-prev-last';
14476             ret[5].cls += ' fc-last';
14477             return ret;
14478             
14479         };
14480         
14481         var cal_table = {
14482             tag: 'table',
14483             cls: 'fc-border-separate',
14484             style : 'width:100%',
14485             cellspacing  : 0,
14486             cn : [
14487                 { 
14488                     tag: 'thead',
14489                     cn : [
14490                         { 
14491                             tag: 'tr',
14492                             cls : 'fc-first fc-last',
14493                             cn : cal_heads()
14494                         }
14495                     ]
14496                 },
14497                 { 
14498                     tag: 'tbody',
14499                     cn : cal_rows()
14500                 }
14501                   
14502             ]
14503         };
14504          
14505          var cfg = {
14506             cls : 'fc fc-ltr',
14507             cn : [
14508                 header,
14509                 {
14510                     cls : 'fc-content',
14511                     style : "position: relative;",
14512                     cn : [
14513                         {
14514                             cls : 'fc-view fc-view-month fc-grid',
14515                             style : 'position: relative',
14516                             unselectable : 'on',
14517                             cn : [
14518                                 {
14519                                     cls : 'fc-event-container',
14520                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14521                                 },
14522                                 cal_table
14523                             ]
14524                         }
14525                     ]
14526     
14527                 }
14528            ] 
14529             
14530         };
14531         
14532          
14533         
14534         return cfg;
14535     },
14536     
14537     
14538     initEvents : function()
14539     {
14540         if(!this.store){
14541             throw "can not find store for calendar";
14542         }
14543         
14544         var mark = {
14545             tag: "div",
14546             cls:"x-dlg-mask",
14547             style: "text-align:center",
14548             cn: [
14549                 {
14550                     tag: "div",
14551                     style: "background-color:white;width:50%;margin:250 auto",
14552                     cn: [
14553                         {
14554                             tag: "img",
14555                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14556                         },
14557                         {
14558                             tag: "span",
14559                             html: "Loading"
14560                         }
14561                         
14562                     ]
14563                 }
14564             ]
14565         }
14566         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14567         
14568         var size = this.el.select('.fc-content', true).first().getSize();
14569         this.maskEl.setSize(size.width, size.height);
14570         this.maskEl.enableDisplayMode("block");
14571         if(!this.loadMask){
14572             this.maskEl.hide();
14573         }
14574         
14575         this.store = Roo.factory(this.store, Roo.data);
14576         this.store.on('load', this.onLoad, this);
14577         this.store.on('beforeload', this.onBeforeLoad, this);
14578         
14579         this.resize();
14580         
14581         this.cells = this.el.select('.fc-day',true);
14582         //Roo.log(this.cells);
14583         this.textNodes = this.el.query('.fc-day-number');
14584         this.cells.addClassOnOver('fc-state-hover');
14585         
14586         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14587         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14588         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14589         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14590         
14591         this.on('monthchange', this.onMonthChange, this);
14592         
14593         this.update(new Date().clearTime());
14594     },
14595     
14596     resize : function() {
14597         var sz  = this.el.getSize();
14598         
14599         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14600         this.el.select('.fc-day-content div',true).setHeight(34);
14601     },
14602     
14603     
14604     // private
14605     showPrevMonth : function(e){
14606         this.update(this.activeDate.add("mo", -1));
14607     },
14608     showToday : function(e){
14609         this.update(new Date().clearTime());
14610     },
14611     // private
14612     showNextMonth : function(e){
14613         this.update(this.activeDate.add("mo", 1));
14614     },
14615
14616     // private
14617     showPrevYear : function(){
14618         this.update(this.activeDate.add("y", -1));
14619     },
14620
14621     // private
14622     showNextYear : function(){
14623         this.update(this.activeDate.add("y", 1));
14624     },
14625
14626     
14627    // private
14628     update : function(date)
14629     {
14630         var vd = this.activeDate;
14631         this.activeDate = date;
14632 //        if(vd && this.el){
14633 //            var t = date.getTime();
14634 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14635 //                Roo.log('using add remove');
14636 //                
14637 //                this.fireEvent('monthchange', this, date);
14638 //                
14639 //                this.cells.removeClass("fc-state-highlight");
14640 //                this.cells.each(function(c){
14641 //                   if(c.dateValue == t){
14642 //                       c.addClass("fc-state-highlight");
14643 //                       setTimeout(function(){
14644 //                            try{c.dom.firstChild.focus();}catch(e){}
14645 //                       }, 50);
14646 //                       return false;
14647 //                   }
14648 //                   return true;
14649 //                });
14650 //                return;
14651 //            }
14652 //        }
14653         
14654         var days = date.getDaysInMonth();
14655         
14656         var firstOfMonth = date.getFirstDateOfMonth();
14657         var startingPos = firstOfMonth.getDay()-this.startDay;
14658         
14659         if(startingPos < this.startDay){
14660             startingPos += 7;
14661         }
14662         
14663         var pm = date.add(Date.MONTH, -1);
14664         var prevStart = pm.getDaysInMonth()-startingPos;
14665 //        
14666         this.cells = this.el.select('.fc-day',true);
14667         this.textNodes = this.el.query('.fc-day-number');
14668         this.cells.addClassOnOver('fc-state-hover');
14669         
14670         var cells = this.cells.elements;
14671         var textEls = this.textNodes;
14672         
14673         Roo.each(cells, function(cell){
14674             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14675         });
14676         
14677         days += startingPos;
14678
14679         // convert everything to numbers so it's fast
14680         var day = 86400000;
14681         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14682         //Roo.log(d);
14683         //Roo.log(pm);
14684         //Roo.log(prevStart);
14685         
14686         var today = new Date().clearTime().getTime();
14687         var sel = date.clearTime().getTime();
14688         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14689         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14690         var ddMatch = this.disabledDatesRE;
14691         var ddText = this.disabledDatesText;
14692         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14693         var ddaysText = this.disabledDaysText;
14694         var format = this.format;
14695         
14696         var setCellClass = function(cal, cell){
14697             cell.row = 0;
14698             cell.events = [];
14699             cell.more = [];
14700             //Roo.log('set Cell Class');
14701             cell.title = "";
14702             var t = d.getTime();
14703             
14704             //Roo.log(d);
14705             
14706             cell.dateValue = t;
14707             if(t == today){
14708                 cell.className += " fc-today";
14709                 cell.className += " fc-state-highlight";
14710                 cell.title = cal.todayText;
14711             }
14712             if(t == sel){
14713                 // disable highlight in other month..
14714                 //cell.className += " fc-state-highlight";
14715                 
14716             }
14717             // disabling
14718             if(t < min) {
14719                 cell.className = " fc-state-disabled";
14720                 cell.title = cal.minText;
14721                 return;
14722             }
14723             if(t > max) {
14724                 cell.className = " fc-state-disabled";
14725                 cell.title = cal.maxText;
14726                 return;
14727             }
14728             if(ddays){
14729                 if(ddays.indexOf(d.getDay()) != -1){
14730                     cell.title = ddaysText;
14731                     cell.className = " fc-state-disabled";
14732                 }
14733             }
14734             if(ddMatch && format){
14735                 var fvalue = d.dateFormat(format);
14736                 if(ddMatch.test(fvalue)){
14737                     cell.title = ddText.replace("%0", fvalue);
14738                     cell.className = " fc-state-disabled";
14739                 }
14740             }
14741             
14742             if (!cell.initialClassName) {
14743                 cell.initialClassName = cell.dom.className;
14744             }
14745             
14746             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14747         };
14748
14749         var i = 0;
14750         
14751         for(; i < startingPos; i++) {
14752             textEls[i].innerHTML = (++prevStart);
14753             d.setDate(d.getDate()+1);
14754             
14755             cells[i].className = "fc-past fc-other-month";
14756             setCellClass(this, cells[i]);
14757         }
14758         
14759         var intDay = 0;
14760         
14761         for(; i < days; i++){
14762             intDay = i - startingPos + 1;
14763             textEls[i].innerHTML = (intDay);
14764             d.setDate(d.getDate()+1);
14765             
14766             cells[i].className = ''; // "x-date-active";
14767             setCellClass(this, cells[i]);
14768         }
14769         var extraDays = 0;
14770         
14771         for(; i < 42; i++) {
14772             textEls[i].innerHTML = (++extraDays);
14773             d.setDate(d.getDate()+1);
14774             
14775             cells[i].className = "fc-future fc-other-month";
14776             setCellClass(this, cells[i]);
14777         }
14778         
14779         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14780         
14781         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14782         
14783         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14784         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14785         
14786         if(totalRows != 6){
14787             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14788             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14789         }
14790         
14791         this.fireEvent('monthchange', this, date);
14792         
14793         
14794         /*
14795         if(!this.internalRender){
14796             var main = this.el.dom.firstChild;
14797             var w = main.offsetWidth;
14798             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14799             Roo.fly(main).setWidth(w);
14800             this.internalRender = true;
14801             // opera does not respect the auto grow header center column
14802             // then, after it gets a width opera refuses to recalculate
14803             // without a second pass
14804             if(Roo.isOpera && !this.secondPass){
14805                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14806                 this.secondPass = true;
14807                 this.update.defer(10, this, [date]);
14808             }
14809         }
14810         */
14811         
14812     },
14813     
14814     findCell : function(dt) {
14815         dt = dt.clearTime().getTime();
14816         var ret = false;
14817         this.cells.each(function(c){
14818             //Roo.log("check " +c.dateValue + '?=' + dt);
14819             if(c.dateValue == dt){
14820                 ret = c;
14821                 return false;
14822             }
14823             return true;
14824         });
14825         
14826         return ret;
14827     },
14828     
14829     findCells : function(ev) {
14830         var s = ev.start.clone().clearTime().getTime();
14831        // Roo.log(s);
14832         var e= ev.end.clone().clearTime().getTime();
14833        // Roo.log(e);
14834         var ret = [];
14835         this.cells.each(function(c){
14836              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14837             
14838             if(c.dateValue > e){
14839                 return ;
14840             }
14841             if(c.dateValue < s){
14842                 return ;
14843             }
14844             ret.push(c);
14845         });
14846         
14847         return ret;    
14848     },
14849     
14850 //    findBestRow: function(cells)
14851 //    {
14852 //        var ret = 0;
14853 //        
14854 //        for (var i =0 ; i < cells.length;i++) {
14855 //            ret  = Math.max(cells[i].rows || 0,ret);
14856 //        }
14857 //        return ret;
14858 //        
14859 //    },
14860     
14861     
14862     addItem : function(ev)
14863     {
14864         // look for vertical location slot in
14865         var cells = this.findCells(ev);
14866         
14867 //        ev.row = this.findBestRow(cells);
14868         
14869         // work out the location.
14870         
14871         var crow = false;
14872         var rows = [];
14873         for(var i =0; i < cells.length; i++) {
14874             
14875             cells[i].row = cells[0].row;
14876             
14877             if(i == 0){
14878                 cells[i].row = cells[i].row + 1;
14879             }
14880             
14881             if (!crow) {
14882                 crow = {
14883                     start : cells[i],
14884                     end :  cells[i]
14885                 };
14886                 continue;
14887             }
14888             if (crow.start.getY() == cells[i].getY()) {
14889                 // on same row.
14890                 crow.end = cells[i];
14891                 continue;
14892             }
14893             // different row.
14894             rows.push(crow);
14895             crow = {
14896                 start: cells[i],
14897                 end : cells[i]
14898             };
14899             
14900         }
14901         
14902         rows.push(crow);
14903         ev.els = [];
14904         ev.rows = rows;
14905         ev.cells = cells;
14906         
14907         cells[0].events.push(ev);
14908         
14909         this.calevents.push(ev);
14910     },
14911     
14912     clearEvents: function() {
14913         
14914         if(!this.calevents){
14915             return;
14916         }
14917         
14918         Roo.each(this.cells.elements, function(c){
14919             c.row = 0;
14920             c.events = [];
14921             c.more = [];
14922         });
14923         
14924         Roo.each(this.calevents, function(e) {
14925             Roo.each(e.els, function(el) {
14926                 el.un('mouseenter' ,this.onEventEnter, this);
14927                 el.un('mouseleave' ,this.onEventLeave, this);
14928                 el.remove();
14929             },this);
14930         },this);
14931         
14932         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14933             e.remove();
14934         });
14935         
14936     },
14937     
14938     renderEvents: function()
14939     {   
14940         var _this = this;
14941         
14942         this.cells.each(function(c) {
14943             
14944             if(c.row < 5){
14945                 return;
14946             }
14947             
14948             var ev = c.events;
14949             
14950             var r = 4;
14951             if(c.row != c.events.length){
14952                 r = 4 - (4 - (c.row - c.events.length));
14953             }
14954             
14955             c.events = ev.slice(0, r);
14956             c.more = ev.slice(r);
14957             
14958             if(c.more.length && c.more.length == 1){
14959                 c.events.push(c.more.pop());
14960             }
14961             
14962             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14963             
14964         });
14965             
14966         this.cells.each(function(c) {
14967             
14968             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14969             
14970             
14971             for (var e = 0; e < c.events.length; e++){
14972                 var ev = c.events[e];
14973                 var rows = ev.rows;
14974                 
14975                 for(var i = 0; i < rows.length; i++) {
14976                 
14977                     // how many rows should it span..
14978
14979                     var  cfg = {
14980                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14981                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14982
14983                         unselectable : "on",
14984                         cn : [
14985                             {
14986                                 cls: 'fc-event-inner',
14987                                 cn : [
14988     //                                {
14989     //                                  tag:'span',
14990     //                                  cls: 'fc-event-time',
14991     //                                  html : cells.length > 1 ? '' : ev.time
14992     //                                },
14993                                     {
14994                                       tag:'span',
14995                                       cls: 'fc-event-title',
14996                                       html : String.format('{0}', ev.title)
14997                                     }
14998
14999
15000                                 ]
15001                             },
15002                             {
15003                                 cls: 'ui-resizable-handle ui-resizable-e',
15004                                 html : '&nbsp;&nbsp;&nbsp'
15005                             }
15006
15007                         ]
15008                     };
15009
15010                     if (i == 0) {
15011                         cfg.cls += ' fc-event-start';
15012                     }
15013                     if ((i+1) == rows.length) {
15014                         cfg.cls += ' fc-event-end';
15015                     }
15016
15017                     var ctr = _this.el.select('.fc-event-container',true).first();
15018                     var cg = ctr.createChild(cfg);
15019
15020                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15021                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15022
15023                     var r = (c.more.length) ? 1 : 0;
15024                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15025                     cg.setWidth(ebox.right - sbox.x -2);
15026
15027                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15028                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15029                     cg.on('click', _this.onEventClick, _this, ev);
15030
15031                     ev.els.push(cg);
15032                     
15033                 }
15034                 
15035             }
15036             
15037             
15038             if(c.more.length){
15039                 var  cfg = {
15040                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15041                     style : 'position: absolute',
15042                     unselectable : "on",
15043                     cn : [
15044                         {
15045                             cls: 'fc-event-inner',
15046                             cn : [
15047                                 {
15048                                   tag:'span',
15049                                   cls: 'fc-event-title',
15050                                   html : 'More'
15051                                 }
15052
15053
15054                             ]
15055                         },
15056                         {
15057                             cls: 'ui-resizable-handle ui-resizable-e',
15058                             html : '&nbsp;&nbsp;&nbsp'
15059                         }
15060
15061                     ]
15062                 };
15063
15064                 var ctr = _this.el.select('.fc-event-container',true).first();
15065                 var cg = ctr.createChild(cfg);
15066
15067                 var sbox = c.select('.fc-day-content',true).first().getBox();
15068                 var ebox = c.select('.fc-day-content',true).first().getBox();
15069                 //Roo.log(cg);
15070                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15071                 cg.setWidth(ebox.right - sbox.x -2);
15072
15073                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15074                 
15075             }
15076             
15077         });
15078         
15079         
15080         
15081     },
15082     
15083     onEventEnter: function (e, el,event,d) {
15084         this.fireEvent('evententer', this, el, event);
15085     },
15086     
15087     onEventLeave: function (e, el,event,d) {
15088         this.fireEvent('eventleave', this, el, event);
15089     },
15090     
15091     onEventClick: function (e, el,event,d) {
15092         this.fireEvent('eventclick', this, el, event);
15093     },
15094     
15095     onMonthChange: function () {
15096         this.store.load();
15097     },
15098     
15099     onMoreEventClick: function(e, el, more)
15100     {
15101         var _this = this;
15102         
15103         this.calpopover.placement = 'right';
15104         this.calpopover.setTitle('More');
15105         
15106         this.calpopover.setContent('');
15107         
15108         var ctr = this.calpopover.el.select('.popover-content', true).first();
15109         
15110         Roo.each(more, function(m){
15111             var cfg = {
15112                 cls : 'fc-event-hori fc-event-draggable',
15113                 html : m.title
15114             }
15115             var cg = ctr.createChild(cfg);
15116             
15117             cg.on('click', _this.onEventClick, _this, m);
15118         });
15119         
15120         this.calpopover.show(el);
15121         
15122         
15123     },
15124     
15125     onLoad: function () 
15126     {   
15127         this.calevents = [];
15128         var cal = this;
15129         
15130         if(this.store.getCount() > 0){
15131             this.store.data.each(function(d){
15132                cal.addItem({
15133                     id : d.data.id,
15134                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15135                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15136                     time : d.data.start_time,
15137                     title : d.data.title,
15138                     description : d.data.description,
15139                     venue : d.data.venue
15140                 });
15141             });
15142         }
15143         
15144         this.renderEvents();
15145         
15146         if(this.calevents.length && this.loadMask){
15147             this.maskEl.hide();
15148         }
15149     },
15150     
15151     onBeforeLoad: function()
15152     {
15153         this.clearEvents();
15154         if(this.loadMask){
15155             this.maskEl.show();
15156         }
15157     }
15158 });
15159
15160  
15161  /*
15162  * - LGPL
15163  *
15164  * element
15165  * 
15166  */
15167
15168 /**
15169  * @class Roo.bootstrap.Popover
15170  * @extends Roo.bootstrap.Component
15171  * Bootstrap Popover class
15172  * @cfg {String} html contents of the popover   (or false to use children..)
15173  * @cfg {String} title of popover (or false to hide)
15174  * @cfg {String} placement how it is placed
15175  * @cfg {String} trigger click || hover (or false to trigger manually)
15176  * @cfg {String} over what (parent or false to trigger manually.)
15177  * @cfg {Number} delay - delay before showing
15178  
15179  * @constructor
15180  * Create a new Popover
15181  * @param {Object} config The config object
15182  */
15183
15184 Roo.bootstrap.Popover = function(config){
15185     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15186 };
15187
15188 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15189     
15190     title: 'Fill in a title',
15191     html: false,
15192     
15193     placement : 'right',
15194     trigger : 'hover', // hover
15195     
15196     delay : 0,
15197     
15198     over: 'parent',
15199     
15200     can_build_overlaid : false,
15201     
15202     getChildContainer : function()
15203     {
15204         return this.el.select('.popover-content',true).first();
15205     },
15206     
15207     getAutoCreate : function(){
15208          Roo.log('make popover?');
15209         var cfg = {
15210            cls : 'popover roo-dynamic',
15211            style: 'display:block',
15212            cn : [
15213                 {
15214                     cls : 'arrow'
15215                 },
15216                 {
15217                     cls : 'popover-inner',
15218                     cn : [
15219                         {
15220                             tag: 'h3',
15221                             cls: 'popover-title',
15222                             html : this.title
15223                         },
15224                         {
15225                             cls : 'popover-content',
15226                             html : this.html
15227                         }
15228                     ]
15229                     
15230                 }
15231            ]
15232         };
15233         
15234         return cfg;
15235     },
15236     setTitle: function(str)
15237     {
15238         this.title = str;
15239         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15240     },
15241     setContent: function(str)
15242     {
15243         this.html = str;
15244         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15245     },
15246     // as it get's added to the bottom of the page.
15247     onRender : function(ct, position)
15248     {
15249         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15250         if(!this.el){
15251             var cfg = Roo.apply({},  this.getAutoCreate());
15252             cfg.id = Roo.id();
15253             
15254             if (this.cls) {
15255                 cfg.cls += ' ' + this.cls;
15256             }
15257             if (this.style) {
15258                 cfg.style = this.style;
15259             }
15260             Roo.log("adding to ")
15261             this.el = Roo.get(document.body).createChild(cfg, position);
15262             Roo.log(this.el);
15263         }
15264         this.initEvents();
15265     },
15266     
15267     initEvents : function()
15268     {
15269         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15270         this.el.enableDisplayMode('block');
15271         this.el.hide();
15272         if (this.over === false) {
15273             return; 
15274         }
15275         if (this.triggers === false) {
15276             return;
15277         }
15278         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15279         var triggers = this.trigger ? this.trigger.split(' ') : [];
15280         Roo.each(triggers, function(trigger) {
15281         
15282             if (trigger == 'click') {
15283                 on_el.on('click', this.toggle, this);
15284             } else if (trigger != 'manual') {
15285                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15286                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15287       
15288                 on_el.on(eventIn  ,this.enter, this);
15289                 on_el.on(eventOut, this.leave, this);
15290             }
15291         }, this);
15292         
15293     },
15294     
15295     
15296     // private
15297     timeout : null,
15298     hoverState : null,
15299     
15300     toggle : function () {
15301         this.hoverState == 'in' ? this.leave() : this.enter();
15302     },
15303     
15304     enter : function () {
15305        
15306     
15307         clearTimeout(this.timeout);
15308     
15309         this.hoverState = 'in';
15310     
15311         if (!this.delay || !this.delay.show) {
15312             this.show();
15313             return;
15314         }
15315         var _t = this;
15316         this.timeout = setTimeout(function () {
15317             if (_t.hoverState == 'in') {
15318                 _t.show();
15319             }
15320         }, this.delay.show)
15321     },
15322     leave : function() {
15323         clearTimeout(this.timeout);
15324     
15325         this.hoverState = 'out';
15326     
15327         if (!this.delay || !this.delay.hide) {
15328             this.hide();
15329             return;
15330         }
15331         var _t = this;
15332         this.timeout = setTimeout(function () {
15333             if (_t.hoverState == 'out') {
15334                 _t.hide();
15335             }
15336         }, this.delay.hide)
15337     },
15338     
15339     show : function (on_el)
15340     {
15341         if (!on_el) {
15342             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15343         }
15344         // set content.
15345         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15346         if (this.html !== false) {
15347             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15348         }
15349         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15350         if (!this.title.length) {
15351             this.el.select('.popover-title',true).hide();
15352         }
15353         
15354         var placement = typeof this.placement == 'function' ?
15355             this.placement.call(this, this.el, on_el) :
15356             this.placement;
15357             
15358         var autoToken = /\s?auto?\s?/i;
15359         var autoPlace = autoToken.test(placement);
15360         if (autoPlace) {
15361             placement = placement.replace(autoToken, '') || 'top';
15362         }
15363         
15364         //this.el.detach()
15365         //this.el.setXY([0,0]);
15366         this.el.show();
15367         this.el.dom.style.display='block';
15368         this.el.addClass(placement);
15369         
15370         //this.el.appendTo(on_el);
15371         
15372         var p = this.getPosition();
15373         var box = this.el.getBox();
15374         
15375         if (autoPlace) {
15376             // fixme..
15377         }
15378         var align = Roo.bootstrap.Popover.alignment[placement];
15379         this.el.alignTo(on_el, align[0],align[1]);
15380         //var arrow = this.el.select('.arrow',true).first();
15381         //arrow.set(align[2], 
15382         
15383         this.el.addClass('in');
15384         
15385         
15386         if (this.el.hasClass('fade')) {
15387             // fade it?
15388         }
15389         
15390     },
15391     hide : function()
15392     {
15393         this.el.setXY([0,0]);
15394         this.el.removeClass('in');
15395         this.el.hide();
15396         this.hoverState = null;
15397         
15398     }
15399     
15400 });
15401
15402 Roo.bootstrap.Popover.alignment = {
15403     'left' : ['r-l', [-10,0], 'right'],
15404     'right' : ['l-r', [10,0], 'left'],
15405     'bottom' : ['t-b', [0,10], 'top'],
15406     'top' : [ 'b-t', [0,-10], 'bottom']
15407 };
15408
15409  /*
15410  * - LGPL
15411  *
15412  * Progress
15413  * 
15414  */
15415
15416 /**
15417  * @class Roo.bootstrap.Progress
15418  * @extends Roo.bootstrap.Component
15419  * Bootstrap Progress class
15420  * @cfg {Boolean} striped striped of the progress bar
15421  * @cfg {Boolean} active animated of the progress bar
15422  * 
15423  * 
15424  * @constructor
15425  * Create a new Progress
15426  * @param {Object} config The config object
15427  */
15428
15429 Roo.bootstrap.Progress = function(config){
15430     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15431 };
15432
15433 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15434     
15435     striped : false,
15436     active: false,
15437     
15438     getAutoCreate : function(){
15439         var cfg = {
15440             tag: 'div',
15441             cls: 'progress'
15442         };
15443         
15444         
15445         if(this.striped){
15446             cfg.cls += ' progress-striped';
15447         }
15448       
15449         if(this.active){
15450             cfg.cls += ' active';
15451         }
15452         
15453         
15454         return cfg;
15455     }
15456    
15457 });
15458
15459  
15460
15461  /*
15462  * - LGPL
15463  *
15464  * ProgressBar
15465  * 
15466  */
15467
15468 /**
15469  * @class Roo.bootstrap.ProgressBar
15470  * @extends Roo.bootstrap.Component
15471  * Bootstrap ProgressBar class
15472  * @cfg {Number} aria_valuenow aria-value now
15473  * @cfg {Number} aria_valuemin aria-value min
15474  * @cfg {Number} aria_valuemax aria-value max
15475  * @cfg {String} label label for the progress bar
15476  * @cfg {String} panel (success | info | warning | danger )
15477  * @cfg {String} role role of the progress bar
15478  * @cfg {String} sr_only text
15479  * 
15480  * 
15481  * @constructor
15482  * Create a new ProgressBar
15483  * @param {Object} config The config object
15484  */
15485
15486 Roo.bootstrap.ProgressBar = function(config){
15487     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15488 };
15489
15490 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15491     
15492     aria_valuenow : 0,
15493     aria_valuemin : 0,
15494     aria_valuemax : 100,
15495     label : false,
15496     panel : false,
15497     role : false,
15498     sr_only: false,
15499     
15500     getAutoCreate : function()
15501     {
15502         
15503         var cfg = {
15504             tag: 'div',
15505             cls: 'progress-bar',
15506             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15507         };
15508         
15509         if(this.sr_only){
15510             cfg.cn = {
15511                 tag: 'span',
15512                 cls: 'sr-only',
15513                 html: this.sr_only
15514             }
15515         }
15516         
15517         if(this.role){
15518             cfg.role = this.role;
15519         }
15520         
15521         if(this.aria_valuenow){
15522             cfg['aria-valuenow'] = this.aria_valuenow;
15523         }
15524         
15525         if(this.aria_valuemin){
15526             cfg['aria-valuemin'] = this.aria_valuemin;
15527         }
15528         
15529         if(this.aria_valuemax){
15530             cfg['aria-valuemax'] = this.aria_valuemax;
15531         }
15532         
15533         if(this.label && !this.sr_only){
15534             cfg.html = this.label;
15535         }
15536         
15537         if(this.panel){
15538             cfg.cls += ' progress-bar-' + this.panel;
15539         }
15540         
15541         return cfg;
15542     },
15543     
15544     update : function(aria_valuenow)
15545     {
15546         this.aria_valuenow = aria_valuenow;
15547         
15548         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15549     }
15550    
15551 });
15552
15553  
15554
15555  /*
15556  * - LGPL
15557  *
15558  * column
15559  * 
15560  */
15561
15562 /**
15563  * @class Roo.bootstrap.TabGroup
15564  * @extends Roo.bootstrap.Column
15565  * Bootstrap Column class
15566  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15567  * @cfg {Boolean} carousel true to make the group behave like a carousel
15568  * @cfg {Number} bullets show the panel pointer.. default 0
15569  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15570  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15571  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15572  * 
15573  * @constructor
15574  * Create a new TabGroup
15575  * @param {Object} config The config object
15576  */
15577
15578 Roo.bootstrap.TabGroup = function(config){
15579     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15580     if (!this.navId) {
15581         this.navId = Roo.id();
15582     }
15583     this.tabs = [];
15584     Roo.bootstrap.TabGroup.register(this);
15585     
15586 };
15587
15588 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15589     
15590     carousel : false,
15591     transition : false,
15592     bullets : 0,
15593     timer : 0,
15594     autoslide : false,
15595     slideFn : false,
15596     slideOnTouch : false,
15597     
15598     getAutoCreate : function()
15599     {
15600         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15601         
15602         cfg.cls += ' tab-content';
15603         
15604         Roo.log('get auto create...............');
15605         
15606         if (this.carousel) {
15607             cfg.cls += ' carousel slide';
15608             
15609             cfg.cn = [{
15610                cls : 'carousel-inner'
15611             }];
15612         
15613             if(this.bullets > 0 && !Roo.isTouch){
15614                 
15615                 var bullets = {
15616                     cls : 'carousel-bullets',
15617                     cn : []
15618                 };
15619                 
15620                 if(this.bullets_cls){
15621                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15622                 }
15623                 
15624                 for (var i = 0; i < this.bullets; i++){
15625                     bullets.cn.push({
15626                         cls : 'bullet bullet-' + i
15627                     });
15628                 }
15629                 
15630                 bullets.cn.push({
15631                     cls : 'clear'
15632                 });
15633                 
15634                 cfg.cn[0].cn = bullets;
15635             }
15636         }
15637         
15638         return cfg;
15639     },
15640     
15641     initEvents:  function()
15642     {
15643         Roo.log('-------- init events on tab group ---------');
15644         
15645         if(this.bullets > 0 && !Roo.isTouch){
15646             this.initBullet();
15647         }
15648         
15649         Roo.log(this);
15650         
15651         if(Roo.isTouch && this.slideOnTouch){
15652             this.el.on("touchstart", this.onTouchStart, this);
15653         }
15654         
15655         if(this.autoslide){
15656             var _this = this;
15657             
15658             this.slideFn = window.setInterval(function() {
15659                 _this.showPanelNext();
15660             }, this.timer);
15661         }
15662         
15663     },
15664     
15665     onTouchStart : function(e, el, o)
15666     {
15667         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15668             return;
15669         }
15670         
15671         this.showPanelNext();
15672     },
15673     
15674     getChildContainer : function()
15675     {
15676         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15677     },
15678     
15679     /**
15680     * register a Navigation item
15681     * @param {Roo.bootstrap.NavItem} the navitem to add
15682     */
15683     register : function(item)
15684     {
15685         this.tabs.push( item);
15686         item.navId = this.navId; // not really needed..
15687     
15688     },
15689     
15690     getActivePanel : function()
15691     {
15692         var r = false;
15693         Roo.each(this.tabs, function(t) {
15694             if (t.active) {
15695                 r = t;
15696                 return false;
15697             }
15698             return null;
15699         });
15700         return r;
15701         
15702     },
15703     getPanelByName : function(n)
15704     {
15705         var r = false;
15706         Roo.each(this.tabs, function(t) {
15707             if (t.tabId == n) {
15708                 r = t;
15709                 return false;
15710             }
15711             return null;
15712         });
15713         return r;
15714     },
15715     indexOfPanel : function(p)
15716     {
15717         var r = false;
15718         Roo.each(this.tabs, function(t,i) {
15719             if (t.tabId == p.tabId) {
15720                 r = i;
15721                 return false;
15722             }
15723             return null;
15724         });
15725         return r;
15726     },
15727     /**
15728      * show a specific panel
15729      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15730      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15731      */
15732     showPanel : function (pan)
15733     {
15734         if(this.transition){
15735             Roo.log("waiting for the transitionend");
15736             return;
15737         }
15738         
15739         if (typeof(pan) == 'number') {
15740             pan = this.tabs[pan];
15741         }
15742         if (typeof(pan) == 'string') {
15743             pan = this.getPanelByName(pan);
15744         }
15745         if (pan.tabId == this.getActivePanel().tabId) {
15746             return true;
15747         }
15748         var cur = this.getActivePanel();
15749         
15750         if (false === cur.fireEvent('beforedeactivate')) {
15751             return false;
15752         }
15753         
15754         if(this.bullets > 0 && !Roo.isTouch){
15755             this.setActiveBullet(this.indexOfPanel(pan));
15756         }
15757         
15758         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15759             
15760             this.transition = true;
15761             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15762             var lr = dir == 'next' ? 'left' : 'right';
15763             pan.el.addClass(dir); // or prev
15764             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15765             cur.el.addClass(lr); // or right
15766             pan.el.addClass(lr);
15767             
15768             var _this = this;
15769             cur.el.on('transitionend', function() {
15770                 Roo.log("trans end?");
15771                 
15772                 pan.el.removeClass([lr,dir]);
15773                 pan.setActive(true);
15774                 
15775                 cur.el.removeClass([lr]);
15776                 cur.setActive(false);
15777                 
15778                 _this.transition = false;
15779                 
15780             }, this, { single:  true } );
15781             
15782             return true;
15783         }
15784         
15785         cur.setActive(false);
15786         pan.setActive(true);
15787         
15788         return true;
15789         
15790     },
15791     showPanelNext : function()
15792     {
15793         var i = this.indexOfPanel(this.getActivePanel());
15794         
15795         if (i >= this.tabs.length - 1 && !this.autoslide) {
15796             return;
15797         }
15798         
15799         if (i >= this.tabs.length - 1 && this.autoslide) {
15800             i = -1;
15801         }
15802         
15803         this.showPanel(this.tabs[i+1]);
15804     },
15805     
15806     showPanelPrev : function()
15807     {
15808         var i = this.indexOfPanel(this.getActivePanel());
15809         
15810         if (i  < 1 && !this.autoslide) {
15811             return;
15812         }
15813         
15814         if (i < 1 && this.autoslide) {
15815             i = this.tabs.length;
15816         }
15817         
15818         this.showPanel(this.tabs[i-1]);
15819     },
15820     
15821     initBullet : function()
15822     {
15823         if(Roo.isTouch){
15824             return;
15825         }
15826         
15827         var _this = this;
15828         
15829         for (var i = 0; i < this.bullets; i++){
15830             var bullet = this.el.select('.bullet-' + i, true).first();
15831
15832             if(!bullet){
15833                 continue;
15834             }
15835
15836             bullet.on('click', (function(e, el, o, ii, t){
15837
15838                 e.preventDefault();
15839
15840                 _this.showPanel(ii);
15841
15842                 if(_this.autoslide && _this.slideFn){
15843                     clearInterval(_this.slideFn);
15844                     _this.slideFn = window.setInterval(function() {
15845                         _this.showPanelNext();
15846                     }, _this.timer);
15847                 }
15848
15849             }).createDelegate(this, [i, bullet], true));
15850         }
15851     },
15852     
15853     setActiveBullet : function(i)
15854     {
15855         if(Roo.isTouch){
15856             return;
15857         }
15858         
15859         Roo.each(this.el.select('.bullet', true).elements, function(el){
15860             el.removeClass('selected');
15861         });
15862
15863         var bullet = this.el.select('.bullet-' + i, true).first();
15864         
15865         if(!bullet){
15866             return;
15867         }
15868         
15869         bullet.addClass('selected');
15870     }
15871     
15872     
15873   
15874 });
15875
15876  
15877
15878  
15879  
15880 Roo.apply(Roo.bootstrap.TabGroup, {
15881     
15882     groups: {},
15883      /**
15884     * register a Navigation Group
15885     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15886     */
15887     register : function(navgrp)
15888     {
15889         this.groups[navgrp.navId] = navgrp;
15890         
15891     },
15892     /**
15893     * fetch a Navigation Group based on the navigation ID
15894     * if one does not exist , it will get created.
15895     * @param {string} the navgroup to add
15896     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15897     */
15898     get: function(navId) {
15899         if (typeof(this.groups[navId]) == 'undefined') {
15900             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15901         }
15902         return this.groups[navId] ;
15903     }
15904     
15905     
15906     
15907 });
15908
15909  /*
15910  * - LGPL
15911  *
15912  * TabPanel
15913  * 
15914  */
15915
15916 /**
15917  * @class Roo.bootstrap.TabPanel
15918  * @extends Roo.bootstrap.Component
15919  * Bootstrap TabPanel class
15920  * @cfg {Boolean} active panel active
15921  * @cfg {String} html panel content
15922  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15923  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15924  * 
15925  * 
15926  * @constructor
15927  * Create a new TabPanel
15928  * @param {Object} config The config object
15929  */
15930
15931 Roo.bootstrap.TabPanel = function(config){
15932     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15933     this.addEvents({
15934         /**
15935              * @event changed
15936              * Fires when the active status changes
15937              * @param {Roo.bootstrap.TabPanel} this
15938              * @param {Boolean} state the new state
15939             
15940          */
15941         'changed': true,
15942         /**
15943              * @event beforedeactivate
15944              * Fires before a tab is de-activated - can be used to do validation on a form.
15945              * @param {Roo.bootstrap.TabPanel} this
15946              * @return {Boolean} false if there is an error
15947             
15948          */
15949         'beforedeactivate': true
15950      });
15951     
15952     this.tabId = this.tabId || Roo.id();
15953   
15954 };
15955
15956 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15957     
15958     active: false,
15959     html: false,
15960     tabId: false,
15961     navId : false,
15962     
15963     getAutoCreate : function(){
15964         var cfg = {
15965             tag: 'div',
15966             // item is needed for carousel - not sure if it has any effect otherwise
15967             cls: 'tab-pane item',
15968             html: this.html || ''
15969         };
15970         
15971         if(this.active){
15972             cfg.cls += ' active';
15973         }
15974         
15975         if(this.tabId){
15976             cfg.tabId = this.tabId;
15977         }
15978         
15979         
15980         return cfg;
15981     },
15982     
15983     initEvents:  function()
15984     {
15985         Roo.log('-------- init events on tab panel ---------');
15986         
15987         var p = this.parent();
15988         this.navId = this.navId || p.navId;
15989         
15990         if (typeof(this.navId) != 'undefined') {
15991             // not really needed.. but just in case.. parent should be a NavGroup.
15992             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15993             Roo.log(['register', tg, this]);
15994             tg.register(this);
15995             
15996             var i = tg.tabs.length - 1;
15997             
15998             if(this.active && tg.bullets > 0 && i < tg.bullets){
15999                 tg.setActiveBullet(i);
16000             }
16001         }
16002         
16003     },
16004     
16005     
16006     onRender : function(ct, position)
16007     {
16008        // Roo.log("Call onRender: " + this.xtype);
16009         
16010         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16011         
16012         
16013         
16014         
16015         
16016     },
16017     
16018     setActive: function(state)
16019     {
16020         Roo.log("panel - set active " + this.tabId + "=" + state);
16021         
16022         this.active = state;
16023         if (!state) {
16024             this.el.removeClass('active');
16025             
16026         } else  if (!this.el.hasClass('active')) {
16027             this.el.addClass('active');
16028         }
16029         
16030         this.fireEvent('changed', this, state);
16031     }
16032     
16033     
16034 });
16035  
16036
16037  
16038
16039  /*
16040  * - LGPL
16041  *
16042  * DateField
16043  * 
16044  */
16045
16046 /**
16047  * @class Roo.bootstrap.DateField
16048  * @extends Roo.bootstrap.Input
16049  * Bootstrap DateField class
16050  * @cfg {Number} weekStart default 0
16051  * @cfg {String} viewMode default empty, (months|years)
16052  * @cfg {String} minViewMode default empty, (months|years)
16053  * @cfg {Number} startDate default -Infinity
16054  * @cfg {Number} endDate default Infinity
16055  * @cfg {Boolean} todayHighlight default false
16056  * @cfg {Boolean} todayBtn default false
16057  * @cfg {Boolean} calendarWeeks default false
16058  * @cfg {Object} daysOfWeekDisabled default empty
16059  * @cfg {Boolean} singleMode default false (true | false)
16060  * 
16061  * @cfg {Boolean} keyboardNavigation default true
16062  * @cfg {String} language default en
16063  * 
16064  * @constructor
16065  * Create a new DateField
16066  * @param {Object} config The config object
16067  */
16068
16069 Roo.bootstrap.DateField = function(config){
16070     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16071      this.addEvents({
16072             /**
16073              * @event show
16074              * Fires when this field show.
16075              * @param {Roo.bootstrap.DateField} this
16076              * @param {Mixed} date The date value
16077              */
16078             show : true,
16079             /**
16080              * @event show
16081              * Fires when this field hide.
16082              * @param {Roo.bootstrap.DateField} this
16083              * @param {Mixed} date The date value
16084              */
16085             hide : true,
16086             /**
16087              * @event select
16088              * Fires when select a date.
16089              * @param {Roo.bootstrap.DateField} this
16090              * @param {Mixed} date The date value
16091              */
16092             select : true
16093         });
16094 };
16095
16096 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16097     
16098     /**
16099      * @cfg {String} format
16100      * The default date format string which can be overriden for localization support.  The format must be
16101      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16102      */
16103     format : "m/d/y",
16104     /**
16105      * @cfg {String} altFormats
16106      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16107      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16108      */
16109     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16110     
16111     weekStart : 0,
16112     
16113     viewMode : '',
16114     
16115     minViewMode : '',
16116     
16117     todayHighlight : false,
16118     
16119     todayBtn: false,
16120     
16121     language: 'en',
16122     
16123     keyboardNavigation: true,
16124     
16125     calendarWeeks: false,
16126     
16127     startDate: -Infinity,
16128     
16129     endDate: Infinity,
16130     
16131     daysOfWeekDisabled: [],
16132     
16133     _events: [],
16134     
16135     singleMode : false,
16136     
16137     UTCDate: function()
16138     {
16139         return new Date(Date.UTC.apply(Date, arguments));
16140     },
16141     
16142     UTCToday: function()
16143     {
16144         var today = new Date();
16145         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16146     },
16147     
16148     getDate: function() {
16149             var d = this.getUTCDate();
16150             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16151     },
16152     
16153     getUTCDate: function() {
16154             return this.date;
16155     },
16156     
16157     setDate: function(d) {
16158             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16159     },
16160     
16161     setUTCDate: function(d) {
16162             this.date = d;
16163             this.setValue(this.formatDate(this.date));
16164     },
16165         
16166     onRender: function(ct, position)
16167     {
16168         
16169         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16170         
16171         this.language = this.language || 'en';
16172         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16173         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16174         
16175         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16176         this.format = this.format || 'm/d/y';
16177         this.isInline = false;
16178         this.isInput = true;
16179         this.component = this.el.select('.add-on', true).first() || false;
16180         this.component = (this.component && this.component.length === 0) ? false : this.component;
16181         this.hasInput = this.component && this.inputEL().length;
16182         
16183         if (typeof(this.minViewMode === 'string')) {
16184             switch (this.minViewMode) {
16185                 case 'months':
16186                     this.minViewMode = 1;
16187                     break;
16188                 case 'years':
16189                     this.minViewMode = 2;
16190                     break;
16191                 default:
16192                     this.minViewMode = 0;
16193                     break;
16194             }
16195         }
16196         
16197         if (typeof(this.viewMode === 'string')) {
16198             switch (this.viewMode) {
16199                 case 'months':
16200                     this.viewMode = 1;
16201                     break;
16202                 case 'years':
16203                     this.viewMode = 2;
16204                     break;
16205                 default:
16206                     this.viewMode = 0;
16207                     break;
16208             }
16209         }
16210                 
16211         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16212         
16213 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16214         
16215         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16216         
16217         this.picker().on('mousedown', this.onMousedown, this);
16218         this.picker().on('click', this.onClick, this);
16219         
16220         this.picker().addClass('datepicker-dropdown');
16221         
16222         this.startViewMode = this.viewMode;
16223         
16224         if(this.singleMode){
16225             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16226                 v.setVisibilityMode(Roo.Element.DISPLAY)
16227                 v.hide();
16228             });
16229             
16230             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16231                 v.setStyle('width', '189px');
16232             });
16233         }
16234         
16235         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16236             if(!this.calendarWeeks){
16237                 v.remove();
16238                 return;
16239             }
16240             
16241             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16242             v.attr('colspan', function(i, val){
16243                 return parseInt(val) + 1;
16244             });
16245         })
16246                         
16247         
16248         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16249         
16250         this.setStartDate(this.startDate);
16251         this.setEndDate(this.endDate);
16252         
16253         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16254         
16255         this.fillDow();
16256         this.fillMonths();
16257         this.update();
16258         this.showMode();
16259         
16260         if(this.isInline) {
16261             this.show();
16262         }
16263     },
16264     
16265     picker : function()
16266     {
16267         return this.pickerEl;
16268 //        return this.el.select('.datepicker', true).first();
16269     },
16270     
16271     fillDow: function()
16272     {
16273         var dowCnt = this.weekStart;
16274         
16275         var dow = {
16276             tag: 'tr',
16277             cn: [
16278                 
16279             ]
16280         };
16281         
16282         if(this.calendarWeeks){
16283             dow.cn.push({
16284                 tag: 'th',
16285                 cls: 'cw',
16286                 html: '&nbsp;'
16287             })
16288         }
16289         
16290         while (dowCnt < this.weekStart + 7) {
16291             dow.cn.push({
16292                 tag: 'th',
16293                 cls: 'dow',
16294                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16295             });
16296         }
16297         
16298         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16299     },
16300     
16301     fillMonths: function()
16302     {    
16303         var i = 0;
16304         var months = this.picker().select('>.datepicker-months td', true).first();
16305         
16306         months.dom.innerHTML = '';
16307         
16308         while (i < 12) {
16309             var month = {
16310                 tag: 'span',
16311                 cls: 'month',
16312                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16313             }
16314             
16315             months.createChild(month);
16316         }
16317         
16318     },
16319     
16320     update: function()
16321     {
16322         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;
16323         
16324         if (this.date < this.startDate) {
16325             this.viewDate = new Date(this.startDate);
16326         } else if (this.date > this.endDate) {
16327             this.viewDate = new Date(this.endDate);
16328         } else {
16329             this.viewDate = new Date(this.date);
16330         }
16331         
16332         this.fill();
16333     },
16334     
16335     fill: function() 
16336     {
16337         var d = new Date(this.viewDate),
16338                 year = d.getUTCFullYear(),
16339                 month = d.getUTCMonth(),
16340                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16341                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16342                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16343                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16344                 currentDate = this.date && this.date.valueOf(),
16345                 today = this.UTCToday();
16346         
16347         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16348         
16349 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16350         
16351 //        this.picker.select('>tfoot th.today').
16352 //                                              .text(dates[this.language].today)
16353 //                                              .toggle(this.todayBtn !== false);
16354     
16355         this.updateNavArrows();
16356         this.fillMonths();
16357                                                 
16358         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16359         
16360         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16361          
16362         prevMonth.setUTCDate(day);
16363         
16364         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16365         
16366         var nextMonth = new Date(prevMonth);
16367         
16368         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16369         
16370         nextMonth = nextMonth.valueOf();
16371         
16372         var fillMonths = false;
16373         
16374         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16375         
16376         while(prevMonth.valueOf() < nextMonth) {
16377             var clsName = '';
16378             
16379             if (prevMonth.getUTCDay() === this.weekStart) {
16380                 if(fillMonths){
16381                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16382                 }
16383                     
16384                 fillMonths = {
16385                     tag: 'tr',
16386                     cn: []
16387                 };
16388                 
16389                 if(this.calendarWeeks){
16390                     // ISO 8601: First week contains first thursday.
16391                     // ISO also states week starts on Monday, but we can be more abstract here.
16392                     var
16393                     // Start of current week: based on weekstart/current date
16394                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16395                     // Thursday of this week
16396                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16397                     // First Thursday of year, year from thursday
16398                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16399                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16400                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16401                     
16402                     fillMonths.cn.push({
16403                         tag: 'td',
16404                         cls: 'cw',
16405                         html: calWeek
16406                     });
16407                 }
16408             }
16409             
16410             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16411                 clsName += ' old';
16412             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16413                 clsName += ' new';
16414             }
16415             if (this.todayHighlight &&
16416                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16417                 prevMonth.getUTCMonth() == today.getMonth() &&
16418                 prevMonth.getUTCDate() == today.getDate()) {
16419                 clsName += ' today';
16420             }
16421             
16422             if (currentDate && prevMonth.valueOf() === currentDate) {
16423                 clsName += ' active';
16424             }
16425             
16426             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16427                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16428                     clsName += ' disabled';
16429             }
16430             
16431             fillMonths.cn.push({
16432                 tag: 'td',
16433                 cls: 'day ' + clsName,
16434                 html: prevMonth.getDate()
16435             })
16436             
16437             prevMonth.setDate(prevMonth.getDate()+1);
16438         }
16439           
16440         var currentYear = this.date && this.date.getUTCFullYear();
16441         var currentMonth = this.date && this.date.getUTCMonth();
16442         
16443         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16444         
16445         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16446             v.removeClass('active');
16447             
16448             if(currentYear === year && k === currentMonth){
16449                 v.addClass('active');
16450             }
16451             
16452             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16453                 v.addClass('disabled');
16454             }
16455             
16456         });
16457         
16458         
16459         year = parseInt(year/10, 10) * 10;
16460         
16461         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16462         
16463         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16464         
16465         year -= 1;
16466         for (var i = -1; i < 11; i++) {
16467             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16468                 tag: 'span',
16469                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16470                 html: year
16471             })
16472             
16473             year += 1;
16474         }
16475     },
16476     
16477     showMode: function(dir) 
16478     {
16479         if (dir) {
16480             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16481         }
16482         
16483         Roo.each(this.picker().select('>div',true).elements, function(v){
16484             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16485             v.hide();
16486         });
16487         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16488     },
16489     
16490     place: function()
16491     {
16492         if(this.isInline) return;
16493         
16494         this.picker().removeClass(['bottom', 'top']);
16495         
16496         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16497             /*
16498              * place to the top of element!
16499              *
16500              */
16501             
16502             this.picker().addClass('top');
16503             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16504             
16505             return;
16506         }
16507         
16508         this.picker().addClass('bottom');
16509         
16510         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16511     },
16512     
16513     parseDate : function(value)
16514     {
16515         if(!value || value instanceof Date){
16516             return value;
16517         }
16518         var v = Date.parseDate(value, this.format);
16519         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16520             v = Date.parseDate(value, 'Y-m-d');
16521         }
16522         if(!v && this.altFormats){
16523             if(!this.altFormatsArray){
16524                 this.altFormatsArray = this.altFormats.split("|");
16525             }
16526             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16527                 v = Date.parseDate(value, this.altFormatsArray[i]);
16528             }
16529         }
16530         return v;
16531     },
16532     
16533     formatDate : function(date, fmt)
16534     {   
16535         return (!date || !(date instanceof Date)) ?
16536         date : date.dateFormat(fmt || this.format);
16537     },
16538     
16539     onFocus : function()
16540     {
16541         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16542         this.show();
16543     },
16544     
16545     onBlur : function()
16546     {
16547         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16548         
16549         var d = this.inputEl().getValue();
16550         
16551         this.setValue(d);
16552                 
16553         this.hide();
16554     },
16555     
16556     show : function()
16557     {
16558         this.picker().show();
16559         this.update();
16560         this.place();
16561         
16562         this.fireEvent('show', this, this.date);
16563     },
16564     
16565     hide : function()
16566     {
16567         if(this.isInline) return;
16568         this.picker().hide();
16569         this.viewMode = this.startViewMode;
16570         this.showMode();
16571         
16572         this.fireEvent('hide', this, this.date);
16573         
16574     },
16575     
16576     onMousedown: function(e)
16577     {
16578         e.stopPropagation();
16579         e.preventDefault();
16580     },
16581     
16582     keyup: function(e)
16583     {
16584         Roo.bootstrap.DateField.superclass.keyup.call(this);
16585         this.update();
16586     },
16587
16588     setValue: function(v)
16589     {
16590         
16591         // v can be a string or a date..
16592         
16593         
16594         var d = new Date(this.parseDate(v) ).clearTime();
16595         
16596         if(isNaN(d.getTime())){
16597             this.date = this.viewDate = '';
16598             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16599             return;
16600         }
16601         
16602         v = this.formatDate(d);
16603         
16604         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16605         
16606         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16607      
16608         this.update();
16609
16610         this.fireEvent('select', this, this.date);
16611         
16612     },
16613     
16614     getValue: function()
16615     {
16616         return this.formatDate(this.date);
16617     },
16618     
16619     fireKey: function(e)
16620     {
16621         if (!this.picker().isVisible()){
16622             if (e.keyCode == 27) // allow escape to hide and re-show picker
16623                 this.show();
16624             return;
16625         }
16626         
16627         var dateChanged = false,
16628         dir, day, month,
16629         newDate, newViewDate;
16630         
16631         switch(e.keyCode){
16632             case 27: // escape
16633                 this.hide();
16634                 e.preventDefault();
16635                 break;
16636             case 37: // left
16637             case 39: // right
16638                 if (!this.keyboardNavigation) break;
16639                 dir = e.keyCode == 37 ? -1 : 1;
16640                 
16641                 if (e.ctrlKey){
16642                     newDate = this.moveYear(this.date, dir);
16643                     newViewDate = this.moveYear(this.viewDate, dir);
16644                 } else if (e.shiftKey){
16645                     newDate = this.moveMonth(this.date, dir);
16646                     newViewDate = this.moveMonth(this.viewDate, dir);
16647                 } else {
16648                     newDate = new Date(this.date);
16649                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16650                     newViewDate = new Date(this.viewDate);
16651                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16652                 }
16653                 if (this.dateWithinRange(newDate)){
16654                     this.date = newDate;
16655                     this.viewDate = newViewDate;
16656                     this.setValue(this.formatDate(this.date));
16657 //                    this.update();
16658                     e.preventDefault();
16659                     dateChanged = true;
16660                 }
16661                 break;
16662             case 38: // up
16663             case 40: // down
16664                 if (!this.keyboardNavigation) break;
16665                 dir = e.keyCode == 38 ? -1 : 1;
16666                 if (e.ctrlKey){
16667                     newDate = this.moveYear(this.date, dir);
16668                     newViewDate = this.moveYear(this.viewDate, dir);
16669                 } else if (e.shiftKey){
16670                     newDate = this.moveMonth(this.date, dir);
16671                     newViewDate = this.moveMonth(this.viewDate, dir);
16672                 } else {
16673                     newDate = new Date(this.date);
16674                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16675                     newViewDate = new Date(this.viewDate);
16676                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16677                 }
16678                 if (this.dateWithinRange(newDate)){
16679                     this.date = newDate;
16680                     this.viewDate = newViewDate;
16681                     this.setValue(this.formatDate(this.date));
16682 //                    this.update();
16683                     e.preventDefault();
16684                     dateChanged = true;
16685                 }
16686                 break;
16687             case 13: // enter
16688                 this.setValue(this.formatDate(this.date));
16689                 this.hide();
16690                 e.preventDefault();
16691                 break;
16692             case 9: // tab
16693                 this.setValue(this.formatDate(this.date));
16694                 this.hide();
16695                 break;
16696             case 16: // shift
16697             case 17: // ctrl
16698             case 18: // alt
16699                 break;
16700             default :
16701                 this.hide();
16702                 
16703         }
16704     },
16705     
16706     
16707     onClick: function(e) 
16708     {
16709         e.stopPropagation();
16710         e.preventDefault();
16711         
16712         var target = e.getTarget();
16713         
16714         if(target.nodeName.toLowerCase() === 'i'){
16715             target = Roo.get(target).dom.parentNode;
16716         }
16717         
16718         var nodeName = target.nodeName;
16719         var className = target.className;
16720         var html = target.innerHTML;
16721         //Roo.log(nodeName);
16722         
16723         switch(nodeName.toLowerCase()) {
16724             case 'th':
16725                 switch(className) {
16726                     case 'switch':
16727                         this.showMode(1);
16728                         break;
16729                     case 'prev':
16730                     case 'next':
16731                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16732                         switch(this.viewMode){
16733                                 case 0:
16734                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16735                                         break;
16736                                 case 1:
16737                                 case 2:
16738                                         this.viewDate = this.moveYear(this.viewDate, dir);
16739                                         break;
16740                         }
16741                         this.fill();
16742                         break;
16743                     case 'today':
16744                         var date = new Date();
16745                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16746 //                        this.fill()
16747                         this.setValue(this.formatDate(this.date));
16748                         
16749                         this.hide();
16750                         break;
16751                 }
16752                 break;
16753             case 'span':
16754                 if (className.indexOf('disabled') < 0) {
16755                     this.viewDate.setUTCDate(1);
16756                     if (className.indexOf('month') > -1) {
16757                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16758                     } else {
16759                         var year = parseInt(html, 10) || 0;
16760                         this.viewDate.setUTCFullYear(year);
16761                         
16762                     }
16763                     
16764                     if(this.singleMode){
16765                         this.setValue(this.formatDate(this.viewDate));
16766                         this.hide();
16767                         return;
16768                     }
16769                     
16770                     this.showMode(-1);
16771                     this.fill();
16772                 }
16773                 break;
16774                 
16775             case 'td':
16776                 //Roo.log(className);
16777                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16778                     var day = parseInt(html, 10) || 1;
16779                     var year = this.viewDate.getUTCFullYear(),
16780                         month = this.viewDate.getUTCMonth();
16781
16782                     if (className.indexOf('old') > -1) {
16783                         if(month === 0 ){
16784                             month = 11;
16785                             year -= 1;
16786                         }else{
16787                             month -= 1;
16788                         }
16789                     } else if (className.indexOf('new') > -1) {
16790                         if (month == 11) {
16791                             month = 0;
16792                             year += 1;
16793                         } else {
16794                             month += 1;
16795                         }
16796                     }
16797                     //Roo.log([year,month,day]);
16798                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16799                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16800 //                    this.fill();
16801                     //Roo.log(this.formatDate(this.date));
16802                     this.setValue(this.formatDate(this.date));
16803                     this.hide();
16804                 }
16805                 break;
16806         }
16807     },
16808     
16809     setStartDate: function(startDate)
16810     {
16811         this.startDate = startDate || -Infinity;
16812         if (this.startDate !== -Infinity) {
16813             this.startDate = this.parseDate(this.startDate);
16814         }
16815         this.update();
16816         this.updateNavArrows();
16817     },
16818
16819     setEndDate: function(endDate)
16820     {
16821         this.endDate = endDate || Infinity;
16822         if (this.endDate !== Infinity) {
16823             this.endDate = this.parseDate(this.endDate);
16824         }
16825         this.update();
16826         this.updateNavArrows();
16827     },
16828     
16829     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16830     {
16831         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16832         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16833             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16834         }
16835         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16836             return parseInt(d, 10);
16837         });
16838         this.update();
16839         this.updateNavArrows();
16840     },
16841     
16842     updateNavArrows: function() 
16843     {
16844         if(this.singleMode){
16845             return;
16846         }
16847         
16848         var d = new Date(this.viewDate),
16849         year = d.getUTCFullYear(),
16850         month = d.getUTCMonth();
16851         
16852         Roo.each(this.picker().select('.prev', true).elements, function(v){
16853             v.show();
16854             switch (this.viewMode) {
16855                 case 0:
16856
16857                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16858                         v.hide();
16859                     }
16860                     break;
16861                 case 1:
16862                 case 2:
16863                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16864                         v.hide();
16865                     }
16866                     break;
16867             }
16868         });
16869         
16870         Roo.each(this.picker().select('.next', true).elements, function(v){
16871             v.show();
16872             switch (this.viewMode) {
16873                 case 0:
16874
16875                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16876                         v.hide();
16877                     }
16878                     break;
16879                 case 1:
16880                 case 2:
16881                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16882                         v.hide();
16883                     }
16884                     break;
16885             }
16886         })
16887     },
16888     
16889     moveMonth: function(date, dir)
16890     {
16891         if (!dir) return date;
16892         var new_date = new Date(date.valueOf()),
16893         day = new_date.getUTCDate(),
16894         month = new_date.getUTCMonth(),
16895         mag = Math.abs(dir),
16896         new_month, test;
16897         dir = dir > 0 ? 1 : -1;
16898         if (mag == 1){
16899             test = dir == -1
16900             // If going back one month, make sure month is not current month
16901             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16902             ? function(){
16903                 return new_date.getUTCMonth() == month;
16904             }
16905             // If going forward one month, make sure month is as expected
16906             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16907             : function(){
16908                 return new_date.getUTCMonth() != new_month;
16909             };
16910             new_month = month + dir;
16911             new_date.setUTCMonth(new_month);
16912             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16913             if (new_month < 0 || new_month > 11)
16914                 new_month = (new_month + 12) % 12;
16915         } else {
16916             // For magnitudes >1, move one month at a time...
16917             for (var i=0; i<mag; i++)
16918                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16919                 new_date = this.moveMonth(new_date, dir);
16920             // ...then reset the day, keeping it in the new month
16921             new_month = new_date.getUTCMonth();
16922             new_date.setUTCDate(day);
16923             test = function(){
16924                 return new_month != new_date.getUTCMonth();
16925             };
16926         }
16927         // Common date-resetting loop -- if date is beyond end of month, make it
16928         // end of month
16929         while (test()){
16930             new_date.setUTCDate(--day);
16931             new_date.setUTCMonth(new_month);
16932         }
16933         return new_date;
16934     },
16935
16936     moveYear: function(date, dir)
16937     {
16938         return this.moveMonth(date, dir*12);
16939     },
16940
16941     dateWithinRange: function(date)
16942     {
16943         return date >= this.startDate && date <= this.endDate;
16944     },
16945
16946     
16947     remove: function() 
16948     {
16949         this.picker().remove();
16950     }
16951    
16952 });
16953
16954 Roo.apply(Roo.bootstrap.DateField,  {
16955     
16956     head : {
16957         tag: 'thead',
16958         cn: [
16959         {
16960             tag: 'tr',
16961             cn: [
16962             {
16963                 tag: 'th',
16964                 cls: 'prev',
16965                 html: '<i class="fa fa-arrow-left"/>'
16966             },
16967             {
16968                 tag: 'th',
16969                 cls: 'switch',
16970                 colspan: '5'
16971             },
16972             {
16973                 tag: 'th',
16974                 cls: 'next',
16975                 html: '<i class="fa fa-arrow-right"/>'
16976             }
16977
16978             ]
16979         }
16980         ]
16981     },
16982     
16983     content : {
16984         tag: 'tbody',
16985         cn: [
16986         {
16987             tag: 'tr',
16988             cn: [
16989             {
16990                 tag: 'td',
16991                 colspan: '7'
16992             }
16993             ]
16994         }
16995         ]
16996     },
16997     
16998     footer : {
16999         tag: 'tfoot',
17000         cn: [
17001         {
17002             tag: 'tr',
17003             cn: [
17004             {
17005                 tag: 'th',
17006                 colspan: '7',
17007                 cls: 'today'
17008             }
17009                     
17010             ]
17011         }
17012         ]
17013     },
17014     
17015     dates:{
17016         en: {
17017             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17018             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17019             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17020             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17021             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17022             today: "Today"
17023         }
17024     },
17025     
17026     modes: [
17027     {
17028         clsName: 'days',
17029         navFnc: 'Month',
17030         navStep: 1
17031     },
17032     {
17033         clsName: 'months',
17034         navFnc: 'FullYear',
17035         navStep: 1
17036     },
17037     {
17038         clsName: 'years',
17039         navFnc: 'FullYear',
17040         navStep: 10
17041     }]
17042 });
17043
17044 Roo.apply(Roo.bootstrap.DateField,  {
17045   
17046     template : {
17047         tag: 'div',
17048         cls: 'datepicker dropdown-menu roo-dynamic',
17049         cn: [
17050         {
17051             tag: 'div',
17052             cls: 'datepicker-days',
17053             cn: [
17054             {
17055                 tag: 'table',
17056                 cls: 'table-condensed',
17057                 cn:[
17058                 Roo.bootstrap.DateField.head,
17059                 {
17060                     tag: 'tbody'
17061                 },
17062                 Roo.bootstrap.DateField.footer
17063                 ]
17064             }
17065             ]
17066         },
17067         {
17068             tag: 'div',
17069             cls: 'datepicker-months',
17070             cn: [
17071             {
17072                 tag: 'table',
17073                 cls: 'table-condensed',
17074                 cn:[
17075                 Roo.bootstrap.DateField.head,
17076                 Roo.bootstrap.DateField.content,
17077                 Roo.bootstrap.DateField.footer
17078                 ]
17079             }
17080             ]
17081         },
17082         {
17083             tag: 'div',
17084             cls: 'datepicker-years',
17085             cn: [
17086             {
17087                 tag: 'table',
17088                 cls: 'table-condensed',
17089                 cn:[
17090                 Roo.bootstrap.DateField.head,
17091                 Roo.bootstrap.DateField.content,
17092                 Roo.bootstrap.DateField.footer
17093                 ]
17094             }
17095             ]
17096         }
17097         ]
17098     }
17099 });
17100
17101  
17102
17103  /*
17104  * - LGPL
17105  *
17106  * TimeField
17107  * 
17108  */
17109
17110 /**
17111  * @class Roo.bootstrap.TimeField
17112  * @extends Roo.bootstrap.Input
17113  * Bootstrap DateField class
17114  * 
17115  * 
17116  * @constructor
17117  * Create a new TimeField
17118  * @param {Object} config The config object
17119  */
17120
17121 Roo.bootstrap.TimeField = function(config){
17122     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17123     this.addEvents({
17124             /**
17125              * @event show
17126              * Fires when this field show.
17127              * @param {Roo.bootstrap.DateField} thisthis
17128              * @param {Mixed} date The date value
17129              */
17130             show : true,
17131             /**
17132              * @event show
17133              * Fires when this field hide.
17134              * @param {Roo.bootstrap.DateField} this
17135              * @param {Mixed} date The date value
17136              */
17137             hide : true,
17138             /**
17139              * @event select
17140              * Fires when select a date.
17141              * @param {Roo.bootstrap.DateField} this
17142              * @param {Mixed} date The date value
17143              */
17144             select : true
17145         });
17146 };
17147
17148 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17149     
17150     /**
17151      * @cfg {String} format
17152      * The default time format string which can be overriden for localization support.  The format must be
17153      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17154      */
17155     format : "H:i",
17156        
17157     onRender: function(ct, position)
17158     {
17159         
17160         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17161                 
17162         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17163         
17164         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17165         
17166         this.pop = this.picker().select('>.datepicker-time',true).first();
17167         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17168         
17169         this.picker().on('mousedown', this.onMousedown, this);
17170         this.picker().on('click', this.onClick, this);
17171         
17172         this.picker().addClass('datepicker-dropdown');
17173     
17174         this.fillTime();
17175         this.update();
17176             
17177         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17178         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17179         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17180         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17181         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17182         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17183
17184     },
17185     
17186     fireKey: function(e){
17187         if (!this.picker().isVisible()){
17188             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17189                 this.show();
17190             }
17191             return;
17192         }
17193
17194         e.preventDefault();
17195         
17196         switch(e.keyCode){
17197             case 27: // escape
17198                 this.hide();
17199                 break;
17200             case 37: // left
17201             case 39: // right
17202                 this.onTogglePeriod();
17203                 break;
17204             case 38: // up
17205                 this.onIncrementMinutes();
17206                 break;
17207             case 40: // down
17208                 this.onDecrementMinutes();
17209                 break;
17210             case 13: // enter
17211             case 9: // tab
17212                 this.setTime();
17213                 break;
17214         }
17215     },
17216     
17217     onClick: function(e) {
17218         e.stopPropagation();
17219         e.preventDefault();
17220     },
17221     
17222     picker : function()
17223     {
17224         return this.el.select('.datepicker', true).first();
17225     },
17226     
17227     fillTime: function()
17228     {    
17229         var time = this.pop.select('tbody', true).first();
17230         
17231         time.dom.innerHTML = '';
17232         
17233         time.createChild({
17234             tag: 'tr',
17235             cn: [
17236                 {
17237                     tag: 'td',
17238                     cn: [
17239                         {
17240                             tag: 'a',
17241                             href: '#',
17242                             cls: 'btn',
17243                             cn: [
17244                                 {
17245                                     tag: 'span',
17246                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17247                                 }
17248                             ]
17249                         } 
17250                     ]
17251                 },
17252                 {
17253                     tag: 'td',
17254                     cls: 'separator'
17255                 },
17256                 {
17257                     tag: 'td',
17258                     cn: [
17259                         {
17260                             tag: 'a',
17261                             href: '#',
17262                             cls: 'btn',
17263                             cn: [
17264                                 {
17265                                     tag: 'span',
17266                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17267                                 }
17268                             ]
17269                         }
17270                     ]
17271                 },
17272                 {
17273                     tag: 'td',
17274                     cls: 'separator'
17275                 }
17276             ]
17277         });
17278         
17279         time.createChild({
17280             tag: 'tr',
17281             cn: [
17282                 {
17283                     tag: 'td',
17284                     cn: [
17285                         {
17286                             tag: 'span',
17287                             cls: 'timepicker-hour',
17288                             html: '00'
17289                         }  
17290                     ]
17291                 },
17292                 {
17293                     tag: 'td',
17294                     cls: 'separator',
17295                     html: ':'
17296                 },
17297                 {
17298                     tag: 'td',
17299                     cn: [
17300                         {
17301                             tag: 'span',
17302                             cls: 'timepicker-minute',
17303                             html: '00'
17304                         }  
17305                     ]
17306                 },
17307                 {
17308                     tag: 'td',
17309                     cls: 'separator'
17310                 },
17311                 {
17312                     tag: 'td',
17313                     cn: [
17314                         {
17315                             tag: 'button',
17316                             type: 'button',
17317                             cls: 'btn btn-primary period',
17318                             html: 'AM'
17319                             
17320                         }
17321                     ]
17322                 }
17323             ]
17324         });
17325         
17326         time.createChild({
17327             tag: 'tr',
17328             cn: [
17329                 {
17330                     tag: 'td',
17331                     cn: [
17332                         {
17333                             tag: 'a',
17334                             href: '#',
17335                             cls: 'btn',
17336                             cn: [
17337                                 {
17338                                     tag: 'span',
17339                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17340                                 }
17341                             ]
17342                         }
17343                     ]
17344                 },
17345                 {
17346                     tag: 'td',
17347                     cls: 'separator'
17348                 },
17349                 {
17350                     tag: 'td',
17351                     cn: [
17352                         {
17353                             tag: 'a',
17354                             href: '#',
17355                             cls: 'btn',
17356                             cn: [
17357                                 {
17358                                     tag: 'span',
17359                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17360                                 }
17361                             ]
17362                         }
17363                     ]
17364                 },
17365                 {
17366                     tag: 'td',
17367                     cls: 'separator'
17368                 }
17369             ]
17370         });
17371         
17372     },
17373     
17374     update: function()
17375     {
17376         
17377         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17378         
17379         this.fill();
17380     },
17381     
17382     fill: function() 
17383     {
17384         var hours = this.time.getHours();
17385         var minutes = this.time.getMinutes();
17386         var period = 'AM';
17387         
17388         if(hours > 11){
17389             period = 'PM';
17390         }
17391         
17392         if(hours == 0){
17393             hours = 12;
17394         }
17395         
17396         
17397         if(hours > 12){
17398             hours = hours - 12;
17399         }
17400         
17401         if(hours < 10){
17402             hours = '0' + hours;
17403         }
17404         
17405         if(minutes < 10){
17406             minutes = '0' + minutes;
17407         }
17408         
17409         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17410         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17411         this.pop.select('button', true).first().dom.innerHTML = period;
17412         
17413     },
17414     
17415     place: function()
17416     {   
17417         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17418         
17419         var cls = ['bottom'];
17420         
17421         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17422             cls.pop();
17423             cls.push('top');
17424         }
17425         
17426         cls.push('right');
17427         
17428         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17429             cls.pop();
17430             cls.push('left');
17431         }
17432         
17433         this.picker().addClass(cls.join('-'));
17434         
17435         var _this = this;
17436         
17437         Roo.each(cls, function(c){
17438             if(c == 'bottom'){
17439                 _this.picker().setTop(_this.inputEl().getHeight());
17440                 return;
17441             }
17442             if(c == 'top'){
17443                 _this.picker().setTop(0 - _this.picker().getHeight());
17444                 return;
17445             }
17446             
17447             if(c == 'left'){
17448                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17449                 return;
17450             }
17451             if(c == 'right'){
17452                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17453                 return;
17454             }
17455         });
17456         
17457     },
17458   
17459     onFocus : function()
17460     {
17461         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17462         this.show();
17463     },
17464     
17465     onBlur : function()
17466     {
17467         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17468         this.hide();
17469     },
17470     
17471     show : function()
17472     {
17473         this.picker().show();
17474         this.pop.show();
17475         this.update();
17476         this.place();
17477         
17478         this.fireEvent('show', this, this.date);
17479     },
17480     
17481     hide : function()
17482     {
17483         this.picker().hide();
17484         this.pop.hide();
17485         
17486         this.fireEvent('hide', this, this.date);
17487     },
17488     
17489     setTime : function()
17490     {
17491         this.hide();
17492         this.setValue(this.time.format(this.format));
17493         
17494         this.fireEvent('select', this, this.date);
17495         
17496         
17497     },
17498     
17499     onMousedown: function(e){
17500         e.stopPropagation();
17501         e.preventDefault();
17502     },
17503     
17504     onIncrementHours: function()
17505     {
17506         Roo.log('onIncrementHours');
17507         this.time = this.time.add(Date.HOUR, 1);
17508         this.update();
17509         
17510     },
17511     
17512     onDecrementHours: function()
17513     {
17514         Roo.log('onDecrementHours');
17515         this.time = this.time.add(Date.HOUR, -1);
17516         this.update();
17517     },
17518     
17519     onIncrementMinutes: function()
17520     {
17521         Roo.log('onIncrementMinutes');
17522         this.time = this.time.add(Date.MINUTE, 1);
17523         this.update();
17524     },
17525     
17526     onDecrementMinutes: function()
17527     {
17528         Roo.log('onDecrementMinutes');
17529         this.time = this.time.add(Date.MINUTE, -1);
17530         this.update();
17531     },
17532     
17533     onTogglePeriod: function()
17534     {
17535         Roo.log('onTogglePeriod');
17536         this.time = this.time.add(Date.HOUR, 12);
17537         this.update();
17538     }
17539     
17540    
17541 });
17542
17543 Roo.apply(Roo.bootstrap.TimeField,  {
17544     
17545     content : {
17546         tag: 'tbody',
17547         cn: [
17548             {
17549                 tag: 'tr',
17550                 cn: [
17551                 {
17552                     tag: 'td',
17553                     colspan: '7'
17554                 }
17555                 ]
17556             }
17557         ]
17558     },
17559     
17560     footer : {
17561         tag: 'tfoot',
17562         cn: [
17563             {
17564                 tag: 'tr',
17565                 cn: [
17566                 {
17567                     tag: 'th',
17568                     colspan: '7',
17569                     cls: '',
17570                     cn: [
17571                         {
17572                             tag: 'button',
17573                             cls: 'btn btn-info ok',
17574                             html: 'OK'
17575                         }
17576                     ]
17577                 }
17578
17579                 ]
17580             }
17581         ]
17582     }
17583 });
17584
17585 Roo.apply(Roo.bootstrap.TimeField,  {
17586   
17587     template : {
17588         tag: 'div',
17589         cls: 'datepicker dropdown-menu',
17590         cn: [
17591             {
17592                 tag: 'div',
17593                 cls: 'datepicker-time',
17594                 cn: [
17595                 {
17596                     tag: 'table',
17597                     cls: 'table-condensed',
17598                     cn:[
17599                     Roo.bootstrap.TimeField.content,
17600                     Roo.bootstrap.TimeField.footer
17601                     ]
17602                 }
17603                 ]
17604             }
17605         ]
17606     }
17607 });
17608
17609  
17610
17611  /*
17612  * - LGPL
17613  *
17614  * MonthField
17615  * 
17616  */
17617
17618 /**
17619  * @class Roo.bootstrap.MonthField
17620  * @extends Roo.bootstrap.Input
17621  * Bootstrap MonthField class
17622  * 
17623  * @cfg {String} language default en
17624  * 
17625  * @constructor
17626  * Create a new MonthField
17627  * @param {Object} config The config object
17628  */
17629
17630 Roo.bootstrap.MonthField = function(config){
17631     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17632     
17633     this.addEvents({
17634         /**
17635          * @event show
17636          * Fires when this field show.
17637          * @param {Roo.bootstrap.MonthField} this
17638          * @param {Mixed} date The date value
17639          */
17640         show : true,
17641         /**
17642          * @event show
17643          * Fires when this field hide.
17644          * @param {Roo.bootstrap.MonthField} this
17645          * @param {Mixed} date The date value
17646          */
17647         hide : true,
17648         /**
17649          * @event select
17650          * Fires when select a date.
17651          * @param {Roo.bootstrap.MonthField} this
17652          * @param {String} oldvalue The old value
17653          * @param {String} newvalue The new value
17654          */
17655         select : true
17656     });
17657 };
17658
17659 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17660     
17661     onRender: function(ct, position)
17662     {
17663         
17664         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17665         
17666         this.language = this.language || 'en';
17667         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17668         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17669         
17670         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17671         this.isInline = false;
17672         this.isInput = true;
17673         this.component = this.el.select('.add-on', true).first() || false;
17674         this.component = (this.component && this.component.length === 0) ? false : this.component;
17675         this.hasInput = this.component && this.inputEL().length;
17676         
17677         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17678         
17679         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17680         
17681         this.picker().on('mousedown', this.onMousedown, this);
17682         this.picker().on('click', this.onClick, this);
17683         
17684         this.picker().addClass('datepicker-dropdown');
17685         
17686         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17687             v.setStyle('width', '189px');
17688         });
17689         
17690         this.fillMonths();
17691         
17692         this.update();
17693         
17694         if(this.isInline) {
17695             this.show();
17696         }
17697         
17698     },
17699     
17700     setValue: function(v, suppressEvent)
17701     {   
17702         var o = this.getValue();
17703         
17704         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17705         
17706         this.update();
17707
17708         if(suppressEvent !== true){
17709             this.fireEvent('select', this, o, v);
17710         }
17711         
17712     },
17713     
17714     getValue: function()
17715     {
17716         return this.value;
17717     },
17718     
17719     onClick: function(e) 
17720     {
17721         e.stopPropagation();
17722         e.preventDefault();
17723         
17724         var target = e.getTarget();
17725         
17726         if(target.nodeName.toLowerCase() === 'i'){
17727             target = Roo.get(target).dom.parentNode;
17728         }
17729         
17730         var nodeName = target.nodeName;
17731         var className = target.className;
17732         var html = target.innerHTML;
17733         
17734         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17735             return;
17736         }
17737         
17738         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17739         
17740         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17741         
17742         this.hide();
17743                         
17744     },
17745     
17746     picker : function()
17747     {
17748         return this.pickerEl;
17749     },
17750     
17751     fillMonths: function()
17752     {    
17753         var i = 0;
17754         var months = this.picker().select('>.datepicker-months td', true).first();
17755         
17756         months.dom.innerHTML = '';
17757         
17758         while (i < 12) {
17759             var month = {
17760                 tag: 'span',
17761                 cls: 'month',
17762                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17763             }
17764             
17765             months.createChild(month);
17766         }
17767         
17768     },
17769     
17770     update: function()
17771     {
17772         var _this = this;
17773         
17774         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17775             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17776         }
17777         
17778         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17779             e.removeClass('active');
17780             
17781             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17782                 e.addClass('active');
17783             }
17784         })
17785     },
17786     
17787     place: function()
17788     {
17789         if(this.isInline) return;
17790         
17791         this.picker().removeClass(['bottom', 'top']);
17792         
17793         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17794             /*
17795              * place to the top of element!
17796              *
17797              */
17798             
17799             this.picker().addClass('top');
17800             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17801             
17802             return;
17803         }
17804         
17805         this.picker().addClass('bottom');
17806         
17807         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17808     },
17809     
17810     onFocus : function()
17811     {
17812         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17813         this.show();
17814     },
17815     
17816     onBlur : function()
17817     {
17818         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17819         
17820         var d = this.inputEl().getValue();
17821         
17822         this.setValue(d);
17823                 
17824         this.hide();
17825     },
17826     
17827     show : function()
17828     {
17829         this.picker().show();
17830         this.picker().select('>.datepicker-months', true).first().show();
17831         this.update();
17832         this.place();
17833         
17834         this.fireEvent('show', this, this.date);
17835     },
17836     
17837     hide : function()
17838     {
17839         if(this.isInline) return;
17840         this.picker().hide();
17841         this.fireEvent('hide', this, this.date);
17842         
17843     },
17844     
17845     onMousedown: function(e)
17846     {
17847         e.stopPropagation();
17848         e.preventDefault();
17849     },
17850     
17851     keyup: function(e)
17852     {
17853         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17854         this.update();
17855     },
17856
17857     fireKey: function(e)
17858     {
17859         if (!this.picker().isVisible()){
17860             if (e.keyCode == 27) // allow escape to hide and re-show picker
17861                 this.show();
17862             return;
17863         }
17864         
17865         var dir;
17866         
17867         switch(e.keyCode){
17868             case 27: // escape
17869                 this.hide();
17870                 e.preventDefault();
17871                 break;
17872             case 37: // left
17873             case 39: // right
17874                 dir = e.keyCode == 37 ? -1 : 1;
17875                 
17876                 this.vIndex = this.vIndex + dir;
17877                 
17878                 if(this.vIndex < 0){
17879                     this.vIndex = 0;
17880                 }
17881                 
17882                 if(this.vIndex > 11){
17883                     this.vIndex = 11;
17884                 }
17885                 
17886                 if(isNaN(this.vIndex)){
17887                     this.vIndex = 0;
17888                 }
17889                 
17890                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17891                 
17892                 break;
17893             case 38: // up
17894             case 40: // down
17895                 
17896                 dir = e.keyCode == 38 ? -1 : 1;
17897                 
17898                 this.vIndex = this.vIndex + dir * 4;
17899                 
17900                 if(this.vIndex < 0){
17901                     this.vIndex = 0;
17902                 }
17903                 
17904                 if(this.vIndex > 11){
17905                     this.vIndex = 11;
17906                 }
17907                 
17908                 if(isNaN(this.vIndex)){
17909                     this.vIndex = 0;
17910                 }
17911                 
17912                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17913                 break;
17914                 
17915             case 13: // enter
17916                 
17917                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17918                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17919                 }
17920                 
17921                 this.hide();
17922                 e.preventDefault();
17923                 break;
17924             case 9: // tab
17925                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17926                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17927                 }
17928                 this.hide();
17929                 break;
17930             case 16: // shift
17931             case 17: // ctrl
17932             case 18: // alt
17933                 break;
17934             default :
17935                 this.hide();
17936                 
17937         }
17938     },
17939     
17940     remove: function() 
17941     {
17942         this.picker().remove();
17943     }
17944    
17945 });
17946
17947 Roo.apply(Roo.bootstrap.MonthField,  {
17948     
17949     content : {
17950         tag: 'tbody',
17951         cn: [
17952         {
17953             tag: 'tr',
17954             cn: [
17955             {
17956                 tag: 'td',
17957                 colspan: '7'
17958             }
17959             ]
17960         }
17961         ]
17962     },
17963     
17964     dates:{
17965         en: {
17966             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17967             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17968         }
17969     }
17970 });
17971
17972 Roo.apply(Roo.bootstrap.MonthField,  {
17973   
17974     template : {
17975         tag: 'div',
17976         cls: 'datepicker dropdown-menu roo-dynamic',
17977         cn: [
17978             {
17979                 tag: 'div',
17980                 cls: 'datepicker-months',
17981                 cn: [
17982                 {
17983                     tag: 'table',
17984                     cls: 'table-condensed',
17985                     cn:[
17986                         Roo.bootstrap.DateField.content
17987                     ]
17988                 }
17989                 ]
17990             }
17991         ]
17992     }
17993 });
17994
17995  
17996
17997  
17998  /*
17999  * - LGPL
18000  *
18001  * CheckBox
18002  * 
18003  */
18004
18005 /**
18006  * @class Roo.bootstrap.CheckBox
18007  * @extends Roo.bootstrap.Input
18008  * Bootstrap CheckBox class
18009  * 
18010  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18011  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18012  * @cfg {String} boxLabel The text that appears beside the checkbox
18013  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18014  * @cfg {Boolean} checked initnal the element
18015  * @cfg {Boolean} inline inline the element (default false)
18016  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18017  * 
18018  * @constructor
18019  * Create a new CheckBox
18020  * @param {Object} config The config object
18021  */
18022
18023 Roo.bootstrap.CheckBox = function(config){
18024     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18025    
18026     this.addEvents({
18027         /**
18028         * @event check
18029         * Fires when the element is checked or unchecked.
18030         * @param {Roo.bootstrap.CheckBox} this This input
18031         * @param {Boolean} checked The new checked value
18032         */
18033        check : true
18034     });
18035     
18036 };
18037
18038 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18039   
18040     inputType: 'checkbox',
18041     inputValue: 1,
18042     valueOff: 0,
18043     boxLabel: false,
18044     checked: false,
18045     weight : false,
18046     inline: false,
18047     
18048     getAutoCreate : function()
18049     {
18050         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18051         
18052         var id = Roo.id();
18053         
18054         var cfg = {};
18055         
18056         cfg.cls = 'form-group ' + this.inputType; //input-group
18057         
18058         if(this.inline){
18059             cfg.cls += ' ' + this.inputType + '-inline';
18060         }
18061         
18062         var input =  {
18063             tag: 'input',
18064             id : id,
18065             type : this.inputType,
18066             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18067             cls : 'roo-' + this.inputType, //'form-box',
18068             placeholder : this.placeholder || ''
18069             
18070         };
18071         
18072         if (this.weight) { // Validity check?
18073             cfg.cls += " " + this.inputType + "-" + this.weight;
18074         }
18075         
18076         if (this.disabled) {
18077             input.disabled=true;
18078         }
18079         
18080         if(this.checked){
18081             input.checked = this.checked;
18082         }
18083         
18084         if (this.name) {
18085             input.name = this.name;
18086         }
18087         
18088         if (this.size) {
18089             input.cls += ' input-' + this.size;
18090         }
18091         
18092         var settings=this;
18093         
18094         ['xs','sm','md','lg'].map(function(size){
18095             if (settings[size]) {
18096                 cfg.cls += ' col-' + size + '-' + settings[size];
18097             }
18098         });
18099         
18100         var inputblock = input;
18101          
18102         if (this.before || this.after) {
18103             
18104             inputblock = {
18105                 cls : 'input-group',
18106                 cn :  [] 
18107             };
18108             
18109             if (this.before) {
18110                 inputblock.cn.push({
18111                     tag :'span',
18112                     cls : 'input-group-addon',
18113                     html : this.before
18114                 });
18115             }
18116             
18117             inputblock.cn.push(input);
18118             
18119             if (this.after) {
18120                 inputblock.cn.push({
18121                     tag :'span',
18122                     cls : 'input-group-addon',
18123                     html : this.after
18124                 });
18125             }
18126             
18127         }
18128         
18129         if (align ==='left' && this.fieldLabel.length) {
18130                 Roo.log("left and has label");
18131                 cfg.cn = [
18132                     
18133                     {
18134                         tag: 'label',
18135                         'for' :  id,
18136                         cls : 'control-label col-md-' + this.labelWidth,
18137                         html : this.fieldLabel
18138                         
18139                     },
18140                     {
18141                         cls : "col-md-" + (12 - this.labelWidth), 
18142                         cn: [
18143                             inputblock
18144                         ]
18145                     }
18146                     
18147                 ];
18148         } else if ( this.fieldLabel.length) {
18149                 Roo.log(" label");
18150                 cfg.cn = [
18151                    
18152                     {
18153                         tag: this.boxLabel ? 'span' : 'label',
18154                         'for': id,
18155                         cls: 'control-label box-input-label',
18156                         //cls : 'input-group-addon',
18157                         html : this.fieldLabel
18158                         
18159                     },
18160                     
18161                     inputblock
18162                     
18163                 ];
18164
18165         } else {
18166             
18167                 Roo.log(" no label && no align");
18168                 cfg.cn = [  inputblock ] ;
18169                 
18170                 
18171         }
18172         if(this.boxLabel){
18173              var boxLabelCfg = {
18174                 tag: 'label',
18175                 //'for': id, // box label is handled by onclick - so no for...
18176                 cls: 'box-label',
18177                 html: this.boxLabel
18178             }
18179             
18180             if(this.tooltip){
18181                 boxLabelCfg.tooltip = this.tooltip;
18182             }
18183              
18184             cfg.cn.push(boxLabelCfg);
18185         }
18186         
18187         
18188        
18189         return cfg;
18190         
18191     },
18192     
18193     /**
18194      * return the real input element.
18195      */
18196     inputEl: function ()
18197     {
18198         return this.el.select('input.roo-' + this.inputType,true).first();
18199     },
18200     
18201     labelEl: function()
18202     {
18203         return this.el.select('label.control-label',true).first();
18204     },
18205     /* depricated... */
18206     
18207     label: function()
18208     {
18209         return this.labelEl();
18210     },
18211     
18212     initEvents : function()
18213     {
18214 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18215         
18216         this.inputEl().on('click', this.onClick,  this);
18217         
18218         if (this.boxLabel) { 
18219             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18220         }
18221         
18222         this.startValue = this.getValue();
18223         
18224         if(this.groupId){
18225             Roo.bootstrap.CheckBox.register(this);
18226         }
18227     },
18228     
18229     onClick : function()
18230     {   
18231         this.setChecked(!this.checked);
18232     },
18233     
18234     setChecked : function(state,suppressEvent)
18235     {
18236         this.startValue = this.getValue();
18237         
18238         if(this.inputType == 'radio'){
18239             
18240             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18241                 e.dom.checked = false;
18242             });
18243             
18244             this.inputEl().dom.checked = true;
18245             
18246             this.inputEl().dom.value = this.inputValue;
18247             
18248             if(suppressEvent !== true){
18249                 this.fireEvent('check', this, true);
18250             }
18251             
18252             this.validate();
18253             
18254             return;
18255         }
18256         
18257         this.checked = state;
18258         
18259         this.inputEl().dom.checked = state;
18260         
18261         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18262         
18263         if(suppressEvent !== true){
18264             this.fireEvent('check', this, state);
18265         }
18266         
18267         this.validate();
18268     },
18269     
18270     getValue : function()
18271     {
18272         if(this.inputType == 'radio'){
18273             return this.getGroupValue();
18274         }
18275         
18276         return this.inputEl().getValue();
18277         
18278     },
18279     
18280     getGroupValue : function()
18281     {
18282         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18283             return '';
18284         }
18285         
18286         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18287     },
18288     
18289     setValue : function(v,suppressEvent)
18290     {
18291         if(this.inputType == 'radio'){
18292             this.setGroupValue(v, suppressEvent);
18293             return;
18294         }
18295         
18296         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18297         
18298         this.validate();
18299     },
18300     
18301     setGroupValue : function(v, suppressEvent)
18302     {
18303         this.startValue = this.getValue();
18304         
18305         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18306             e.dom.checked = false;
18307             
18308             if(e.dom.value == v){
18309                 e.dom.checked = true;
18310             }
18311         });
18312         
18313         if(suppressEvent !== true){
18314             this.fireEvent('check', this, true);
18315         }
18316
18317         this.validate();
18318         
18319         return;
18320     },
18321     
18322     validate : function()
18323     {
18324         if(
18325                 this.disabled || 
18326                 (this.inputType == 'radio' && this.validateRadio()) ||
18327                 (this.inputType == 'checkbox' && this.validateCheckbox())
18328         ){
18329             this.markValid();
18330             return true;
18331         }
18332         
18333         this.markInvalid();
18334         return false;
18335     },
18336     
18337     validateRadio : function()
18338     {
18339         var valid = false;
18340         
18341         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18342             if(!e.dom.checked){
18343                 return;
18344             }
18345             
18346             valid = true;
18347             
18348             return false;
18349         });
18350         
18351         return valid;
18352     },
18353     
18354     validateCheckbox : function()
18355     {
18356         if(!this.groupId){
18357             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18358         }
18359         
18360         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18361         
18362         if(!group){
18363             return false;
18364         }
18365         
18366         var r = false;
18367         
18368         for(var i in group){
18369             if(r){
18370                 break;
18371             }
18372             
18373             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18374         }
18375         
18376         return r;
18377     },
18378     
18379     /**
18380      * Mark this field as valid
18381      */
18382     markValid : function()
18383     {
18384         if(this.allowBlank){
18385             return;
18386         }
18387         
18388         var _this = this;
18389         
18390         this.fireEvent('valid', this);
18391         
18392         if(this.inputType == 'radio'){
18393             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18394                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18395                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18396             });
18397             
18398             return;
18399         }
18400         
18401         if(!this.groupId){
18402             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18403             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18404             return;
18405         }
18406         
18407         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18408             
18409         if(!group){
18410             return;
18411         }
18412         
18413         for(var i in group){
18414             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18415             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18416         }
18417     },
18418     
18419      /**
18420      * Mark this field as invalid
18421      * @param {String} msg The validation message
18422      */
18423     markInvalid : function(msg)
18424     {
18425         if(this.allowBlank){
18426             return;
18427         }
18428         
18429         var _this = this;
18430         
18431         this.fireEvent('invalid', this, msg);
18432         
18433         if(this.inputType == 'radio'){
18434             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18435                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18436                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18437             });
18438             
18439             return;
18440         }
18441         
18442         if(!this.groupId){
18443             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18444             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18445             return;
18446         }
18447         
18448         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18449             
18450         if(!group){
18451             return;
18452         }
18453         
18454         for(var i in group){
18455             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18456             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18457         }
18458         
18459     }
18460     
18461 });
18462
18463 Roo.apply(Roo.bootstrap.CheckBox, {
18464     
18465     groups: {},
18466     
18467      /**
18468     * register a CheckBox Group
18469     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18470     */
18471     register : function(checkbox)
18472     {
18473         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18474             this.groups[checkbox.groupId] = {};
18475         }
18476         
18477         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18478             return;
18479         }
18480         
18481         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18482         
18483     },
18484     /**
18485     * fetch a CheckBox Group based on the group ID
18486     * @param {string} the group ID
18487     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18488     */
18489     get: function(groupId) {
18490         if (typeof(this.groups[groupId]) == 'undefined') {
18491             return false;
18492         }
18493         
18494         return this.groups[groupId] ;
18495     }
18496     
18497     
18498 });
18499 /*
18500  * - LGPL
18501  *
18502  * Radio
18503  *
18504  *
18505  * not inline
18506  *<div class="radio">
18507   <label>
18508     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18509     Option one is this and that&mdash;be sure to include why it's great
18510   </label>
18511 </div>
18512  *
18513  *
18514  *inline
18515  *<span>
18516  *<label class="radio-inline">fieldLabel</label>
18517  *<label class="radio-inline">
18518   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18519 </label>
18520 <span>
18521  * 
18522  * 
18523  */
18524
18525 /**
18526  * @class Roo.bootstrap.Radio
18527  * @extends Roo.bootstrap.CheckBox
18528  * Bootstrap Radio class
18529
18530  * @constructor
18531  * Create a new Radio
18532  * @param {Object} config The config object
18533  */
18534
18535 Roo.bootstrap.Radio = function(config){
18536     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18537    
18538 };
18539
18540 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18541     
18542     inputType: 'radio',
18543     inputValue: '',
18544     valueOff: '',
18545     
18546     getAutoCreate : function()
18547     {
18548         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18549         align = align || 'left'; // default...
18550         
18551         
18552         
18553         var id = Roo.id();
18554         
18555         var cfg = {
18556                 tag : this.inline ? 'span' : 'div',
18557                 cls : '',
18558                 cn : []
18559         };
18560         
18561         var inline = this.inline ? ' radio-inline' : '';
18562         
18563         var lbl = {
18564                 tag: 'label' ,
18565                 // does not need for, as we wrap the input with it..
18566                 'for' : id,
18567                 cls : 'control-label box-label' + inline,
18568                 cn : []
18569         };
18570         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18571         
18572         var fieldLabel = {
18573             tag: 'label' ,
18574             //cls : 'control-label' + inline,
18575             html : this.fieldLabel,
18576             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18577         };
18578         
18579  
18580         
18581         
18582         var input =  {
18583             tag: 'input',
18584             id : id,
18585             type : this.inputType,
18586             //value : (!this.checked) ? this.valueOff : this.inputValue,
18587             value : this.inputValue,
18588             cls : 'roo-radio',
18589             placeholder : this.placeholder || '' // ?? needed????
18590             
18591         };
18592         if (this.weight) { // Validity check?
18593             input.cls += " radio-" + this.weight;
18594         }
18595         if (this.disabled) {
18596             input.disabled=true;
18597         }
18598         
18599         if(this.checked){
18600             input.checked = this.checked;
18601         }
18602         
18603         if (this.name) {
18604             input.name = this.name;
18605         }
18606         
18607         if (this.size) {
18608             input.cls += ' input-' + this.size;
18609         }
18610         
18611         //?? can span's inline have a width??
18612         
18613         var settings=this;
18614         ['xs','sm','md','lg'].map(function(size){
18615             if (settings[size]) {
18616                 cfg.cls += ' col-' + size + '-' + settings[size];
18617             }
18618         });
18619         
18620         var inputblock = input;
18621         
18622         if (this.before || this.after) {
18623             
18624             inputblock = {
18625                 cls : 'input-group',
18626                 tag : 'span',
18627                 cn :  [] 
18628             };
18629             if (this.before) {
18630                 inputblock.cn.push({
18631                     tag :'span',
18632                     cls : 'input-group-addon',
18633                     html : this.before
18634                 });
18635             }
18636             inputblock.cn.push(input);
18637             if (this.after) {
18638                 inputblock.cn.push({
18639                     tag :'span',
18640                     cls : 'input-group-addon',
18641                     html : this.after
18642                 });
18643             }
18644             
18645         };
18646         
18647         
18648         if (this.fieldLabel && this.fieldLabel.length) {
18649             cfg.cn.push(fieldLabel);
18650         }
18651        
18652         // normal bootstrap puts the input inside the label.
18653         // however with our styled version - it has to go after the input.
18654        
18655         //lbl.cn.push(inputblock);
18656         
18657         var lblwrap =  {
18658             tag: 'span',
18659             cls: 'radio' + inline,
18660             cn: [
18661                 inputblock,
18662                 lbl
18663             ]
18664         };
18665         
18666         cfg.cn.push( lblwrap);
18667         
18668         if(this.boxLabel){
18669             lbl.cn.push({
18670                 tag: 'span',
18671                 html: this.boxLabel
18672             })
18673         }
18674          
18675         
18676         return cfg;
18677         
18678     },
18679     
18680     initEvents : function()
18681     {
18682 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18683         
18684         this.inputEl().on('click', this.onClick,  this);
18685         if (this.boxLabel) {
18686             Roo.log('find label')
18687             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18688         }
18689         
18690     },
18691     
18692     inputEl: function ()
18693     {
18694         return this.el.select('input.roo-radio',true).first();
18695     },
18696     onClick : function()
18697     {   
18698         Roo.log("click");
18699         this.setChecked(true);
18700     },
18701     
18702     setChecked : function(state,suppressEvent)
18703     {
18704         if(state){
18705             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18706                 v.dom.checked = false;
18707             });
18708         }
18709         Roo.log(this.inputEl().dom);
18710         this.checked = state;
18711         this.inputEl().dom.checked = state;
18712         
18713         if(suppressEvent !== true){
18714             this.fireEvent('check', this, state);
18715         }
18716         
18717         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18718         
18719     },
18720     
18721     getGroupValue : function()
18722     {
18723         var value = '';
18724         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18725             if(v.dom.checked == true){
18726                 value = v.dom.value;
18727             }
18728         });
18729         
18730         return value;
18731     },
18732     
18733     /**
18734      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18735      * @return {Mixed} value The field value
18736      */
18737     getValue : function(){
18738         return this.getGroupValue();
18739     }
18740     
18741 });
18742
18743  
18744 //<script type="text/javascript">
18745
18746 /*
18747  * Based  Ext JS Library 1.1.1
18748  * Copyright(c) 2006-2007, Ext JS, LLC.
18749  * LGPL
18750  *
18751  */
18752  
18753 /**
18754  * @class Roo.HtmlEditorCore
18755  * @extends Roo.Component
18756  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18757  *
18758  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18759  */
18760
18761 Roo.HtmlEditorCore = function(config){
18762     
18763     
18764     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18765     
18766     
18767     this.addEvents({
18768         /**
18769          * @event initialize
18770          * Fires when the editor is fully initialized (including the iframe)
18771          * @param {Roo.HtmlEditorCore} this
18772          */
18773         initialize: true,
18774         /**
18775          * @event activate
18776          * Fires when the editor is first receives the focus. Any insertion must wait
18777          * until after this event.
18778          * @param {Roo.HtmlEditorCore} this
18779          */
18780         activate: true,
18781          /**
18782          * @event beforesync
18783          * Fires before the textarea is updated with content from the editor iframe. Return false
18784          * to cancel the sync.
18785          * @param {Roo.HtmlEditorCore} this
18786          * @param {String} html
18787          */
18788         beforesync: true,
18789          /**
18790          * @event beforepush
18791          * Fires before the iframe editor is updated with content from the textarea. Return false
18792          * to cancel the push.
18793          * @param {Roo.HtmlEditorCore} this
18794          * @param {String} html
18795          */
18796         beforepush: true,
18797          /**
18798          * @event sync
18799          * Fires when the textarea is updated with content from the editor iframe.
18800          * @param {Roo.HtmlEditorCore} this
18801          * @param {String} html
18802          */
18803         sync: true,
18804          /**
18805          * @event push
18806          * Fires when the iframe editor is updated with content from the textarea.
18807          * @param {Roo.HtmlEditorCore} this
18808          * @param {String} html
18809          */
18810         push: true,
18811         
18812         /**
18813          * @event editorevent
18814          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18815          * @param {Roo.HtmlEditorCore} this
18816          */
18817         editorevent: true
18818         
18819     });
18820     
18821     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18822     
18823     // defaults : white / black...
18824     this.applyBlacklists();
18825     
18826     
18827     
18828 };
18829
18830
18831 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18832
18833
18834      /**
18835      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18836      */
18837     
18838     owner : false,
18839     
18840      /**
18841      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18842      *                        Roo.resizable.
18843      */
18844     resizable : false,
18845      /**
18846      * @cfg {Number} height (in pixels)
18847      */   
18848     height: 300,
18849    /**
18850      * @cfg {Number} width (in pixels)
18851      */   
18852     width: 500,
18853     
18854     /**
18855      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18856      * 
18857      */
18858     stylesheets: false,
18859     
18860     // id of frame..
18861     frameId: false,
18862     
18863     // private properties
18864     validationEvent : false,
18865     deferHeight: true,
18866     initialized : false,
18867     activated : false,
18868     sourceEditMode : false,
18869     onFocus : Roo.emptyFn,
18870     iframePad:3,
18871     hideMode:'offsets',
18872     
18873     clearUp: true,
18874     
18875     // blacklist + whitelisted elements..
18876     black: false,
18877     white: false,
18878      
18879     
18880
18881     /**
18882      * Protected method that will not generally be called directly. It
18883      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18884      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18885      */
18886     getDocMarkup : function(){
18887         // body styles..
18888         var st = '';
18889         
18890         // inherit styels from page...?? 
18891         if (this.stylesheets === false) {
18892             
18893             Roo.get(document.head).select('style').each(function(node) {
18894                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18895             });
18896             
18897             Roo.get(document.head).select('link').each(function(node) { 
18898                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18899             });
18900             
18901         } else if (!this.stylesheets.length) {
18902                 // simple..
18903                 st = '<style type="text/css">' +
18904                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18905                    '</style>';
18906         } else { 
18907             
18908         }
18909         
18910         st +=  '<style type="text/css">' +
18911             'IMG { cursor: pointer } ' +
18912         '</style>';
18913
18914         
18915         return '<html><head>' + st  +
18916             //<style type="text/css">' +
18917             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18918             //'</style>' +
18919             ' </head><body class="roo-htmleditor-body"></body></html>';
18920     },
18921
18922     // private
18923     onRender : function(ct, position)
18924     {
18925         var _t = this;
18926         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18927         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18928         
18929         
18930         this.el.dom.style.border = '0 none';
18931         this.el.dom.setAttribute('tabIndex', -1);
18932         this.el.addClass('x-hidden hide');
18933         
18934         
18935         
18936         if(Roo.isIE){ // fix IE 1px bogus margin
18937             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18938         }
18939        
18940         
18941         this.frameId = Roo.id();
18942         
18943          
18944         
18945         var iframe = this.owner.wrap.createChild({
18946             tag: 'iframe',
18947             cls: 'form-control', // bootstrap..
18948             id: this.frameId,
18949             name: this.frameId,
18950             frameBorder : 'no',
18951             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18952         }, this.el
18953         );
18954         
18955         
18956         this.iframe = iframe.dom;
18957
18958          this.assignDocWin();
18959         
18960         this.doc.designMode = 'on';
18961        
18962         this.doc.open();
18963         this.doc.write(this.getDocMarkup());
18964         this.doc.close();
18965
18966         
18967         var task = { // must defer to wait for browser to be ready
18968             run : function(){
18969                 //console.log("run task?" + this.doc.readyState);
18970                 this.assignDocWin();
18971                 if(this.doc.body || this.doc.readyState == 'complete'){
18972                     try {
18973                         this.doc.designMode="on";
18974                     } catch (e) {
18975                         return;
18976                     }
18977                     Roo.TaskMgr.stop(task);
18978                     this.initEditor.defer(10, this);
18979                 }
18980             },
18981             interval : 10,
18982             duration: 10000,
18983             scope: this
18984         };
18985         Roo.TaskMgr.start(task);
18986
18987     },
18988
18989     // private
18990     onResize : function(w, h)
18991     {
18992          Roo.log('resize: ' +w + ',' + h );
18993         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18994         if(!this.iframe){
18995             return;
18996         }
18997         if(typeof w == 'number'){
18998             
18999             this.iframe.style.width = w + 'px';
19000         }
19001         if(typeof h == 'number'){
19002             
19003             this.iframe.style.height = h + 'px';
19004             if(this.doc){
19005                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19006             }
19007         }
19008         
19009     },
19010
19011     /**
19012      * Toggles the editor between standard and source edit mode.
19013      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19014      */
19015     toggleSourceEdit : function(sourceEditMode){
19016         
19017         this.sourceEditMode = sourceEditMode === true;
19018         
19019         if(this.sourceEditMode){
19020  
19021             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19022             
19023         }else{
19024             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19025             //this.iframe.className = '';
19026             this.deferFocus();
19027         }
19028         //this.setSize(this.owner.wrap.getSize());
19029         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19030     },
19031
19032     
19033   
19034
19035     /**
19036      * Protected method that will not generally be called directly. If you need/want
19037      * custom HTML cleanup, this is the method you should override.
19038      * @param {String} html The HTML to be cleaned
19039      * return {String} The cleaned HTML
19040      */
19041     cleanHtml : function(html){
19042         html = String(html);
19043         if(html.length > 5){
19044             if(Roo.isSafari){ // strip safari nonsense
19045                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19046             }
19047         }
19048         if(html == '&nbsp;'){
19049             html = '';
19050         }
19051         return html;
19052     },
19053
19054     /**
19055      * HTML Editor -> Textarea
19056      * Protected method that will not generally be called directly. Syncs the contents
19057      * of the editor iframe with the textarea.
19058      */
19059     syncValue : function(){
19060         if(this.initialized){
19061             var bd = (this.doc.body || this.doc.documentElement);
19062             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19063             var html = bd.innerHTML;
19064             if(Roo.isSafari){
19065                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19066                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19067                 if(m && m[1]){
19068                     html = '<div style="'+m[0]+'">' + html + '</div>';
19069                 }
19070             }
19071             html = this.cleanHtml(html);
19072             // fix up the special chars.. normaly like back quotes in word...
19073             // however we do not want to do this with chinese..
19074             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19075                 var cc = b.charCodeAt();
19076                 if (
19077                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19078                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19079                     (cc >= 0xf900 && cc < 0xfb00 )
19080                 ) {
19081                         return b;
19082                 }
19083                 return "&#"+cc+";" 
19084             });
19085             if(this.owner.fireEvent('beforesync', this, html) !== false){
19086                 this.el.dom.value = html;
19087                 this.owner.fireEvent('sync', this, html);
19088             }
19089         }
19090     },
19091
19092     /**
19093      * Protected method that will not generally be called directly. Pushes the value of the textarea
19094      * into the iframe editor.
19095      */
19096     pushValue : function(){
19097         if(this.initialized){
19098             var v = this.el.dom.value.trim();
19099             
19100 //            if(v.length < 1){
19101 //                v = '&#160;';
19102 //            }
19103             
19104             if(this.owner.fireEvent('beforepush', this, v) !== false){
19105                 var d = (this.doc.body || this.doc.documentElement);
19106                 d.innerHTML = v;
19107                 this.cleanUpPaste();
19108                 this.el.dom.value = d.innerHTML;
19109                 this.owner.fireEvent('push', this, v);
19110             }
19111         }
19112     },
19113
19114     // private
19115     deferFocus : function(){
19116         this.focus.defer(10, this);
19117     },
19118
19119     // doc'ed in Field
19120     focus : function(){
19121         if(this.win && !this.sourceEditMode){
19122             this.win.focus();
19123         }else{
19124             this.el.focus();
19125         }
19126     },
19127     
19128     assignDocWin: function()
19129     {
19130         var iframe = this.iframe;
19131         
19132          if(Roo.isIE){
19133             this.doc = iframe.contentWindow.document;
19134             this.win = iframe.contentWindow;
19135         } else {
19136 //            if (!Roo.get(this.frameId)) {
19137 //                return;
19138 //            }
19139 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19140 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19141             
19142             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19143                 return;
19144             }
19145             
19146             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19147             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19148         }
19149     },
19150     
19151     // private
19152     initEditor : function(){
19153         //console.log("INIT EDITOR");
19154         this.assignDocWin();
19155         
19156         
19157         
19158         this.doc.designMode="on";
19159         this.doc.open();
19160         this.doc.write(this.getDocMarkup());
19161         this.doc.close();
19162         
19163         var dbody = (this.doc.body || this.doc.documentElement);
19164         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19165         // this copies styles from the containing element into thsi one..
19166         // not sure why we need all of this..
19167         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19168         
19169         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19170         //ss['background-attachment'] = 'fixed'; // w3c
19171         dbody.bgProperties = 'fixed'; // ie
19172         //Roo.DomHelper.applyStyles(dbody, ss);
19173         Roo.EventManager.on(this.doc, {
19174             //'mousedown': this.onEditorEvent,
19175             'mouseup': this.onEditorEvent,
19176             'dblclick': this.onEditorEvent,
19177             'click': this.onEditorEvent,
19178             'keyup': this.onEditorEvent,
19179             buffer:100,
19180             scope: this
19181         });
19182         if(Roo.isGecko){
19183             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19184         }
19185         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19186             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19187         }
19188         this.initialized = true;
19189
19190         this.owner.fireEvent('initialize', this);
19191         this.pushValue();
19192     },
19193
19194     // private
19195     onDestroy : function(){
19196         
19197         
19198         
19199         if(this.rendered){
19200             
19201             //for (var i =0; i < this.toolbars.length;i++) {
19202             //    // fixme - ask toolbars for heights?
19203             //    this.toolbars[i].onDestroy();
19204            // }
19205             
19206             //this.wrap.dom.innerHTML = '';
19207             //this.wrap.remove();
19208         }
19209     },
19210
19211     // private
19212     onFirstFocus : function(){
19213         
19214         this.assignDocWin();
19215         
19216         
19217         this.activated = true;
19218          
19219     
19220         if(Roo.isGecko){ // prevent silly gecko errors
19221             this.win.focus();
19222             var s = this.win.getSelection();
19223             if(!s.focusNode || s.focusNode.nodeType != 3){
19224                 var r = s.getRangeAt(0);
19225                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19226                 r.collapse(true);
19227                 this.deferFocus();
19228             }
19229             try{
19230                 this.execCmd('useCSS', true);
19231                 this.execCmd('styleWithCSS', false);
19232             }catch(e){}
19233         }
19234         this.owner.fireEvent('activate', this);
19235     },
19236
19237     // private
19238     adjustFont: function(btn){
19239         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19240         //if(Roo.isSafari){ // safari
19241         //    adjust *= 2;
19242        // }
19243         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19244         if(Roo.isSafari){ // safari
19245             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19246             v =  (v < 10) ? 10 : v;
19247             v =  (v > 48) ? 48 : v;
19248             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19249             
19250         }
19251         
19252         
19253         v = Math.max(1, v+adjust);
19254         
19255         this.execCmd('FontSize', v  );
19256     },
19257
19258     onEditorEvent : function(e)
19259     {
19260         this.owner.fireEvent('editorevent', this, e);
19261       //  this.updateToolbar();
19262         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19263     },
19264
19265     insertTag : function(tg)
19266     {
19267         // could be a bit smarter... -> wrap the current selected tRoo..
19268         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19269             
19270             range = this.createRange(this.getSelection());
19271             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19272             wrappingNode.appendChild(range.extractContents());
19273             range.insertNode(wrappingNode);
19274
19275             return;
19276             
19277             
19278             
19279         }
19280         this.execCmd("formatblock",   tg);
19281         
19282     },
19283     
19284     insertText : function(txt)
19285     {
19286         
19287         
19288         var range = this.createRange();
19289         range.deleteContents();
19290                //alert(Sender.getAttribute('label'));
19291                
19292         range.insertNode(this.doc.createTextNode(txt));
19293     } ,
19294     
19295      
19296
19297     /**
19298      * Executes a Midas editor command on the editor document and performs necessary focus and
19299      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19300      * @param {String} cmd The Midas command
19301      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19302      */
19303     relayCmd : function(cmd, value){
19304         this.win.focus();
19305         this.execCmd(cmd, value);
19306         this.owner.fireEvent('editorevent', this);
19307         //this.updateToolbar();
19308         this.owner.deferFocus();
19309     },
19310
19311     /**
19312      * Executes a Midas editor command directly on the editor document.
19313      * For visual commands, you should use {@link #relayCmd} instead.
19314      * <b>This should only be called after the editor is initialized.</b>
19315      * @param {String} cmd The Midas command
19316      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19317      */
19318     execCmd : function(cmd, value){
19319         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19320         this.syncValue();
19321     },
19322  
19323  
19324    
19325     /**
19326      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19327      * to insert tRoo.
19328      * @param {String} text | dom node.. 
19329      */
19330     insertAtCursor : function(text)
19331     {
19332         
19333         
19334         
19335         if(!this.activated){
19336             return;
19337         }
19338         /*
19339         if(Roo.isIE){
19340             this.win.focus();
19341             var r = this.doc.selection.createRange();
19342             if(r){
19343                 r.collapse(true);
19344                 r.pasteHTML(text);
19345                 this.syncValue();
19346                 this.deferFocus();
19347             
19348             }
19349             return;
19350         }
19351         */
19352         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19353             this.win.focus();
19354             
19355             
19356             // from jquery ui (MIT licenced)
19357             var range, node;
19358             var win = this.win;
19359             
19360             if (win.getSelection && win.getSelection().getRangeAt) {
19361                 range = win.getSelection().getRangeAt(0);
19362                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19363                 range.insertNode(node);
19364             } else if (win.document.selection && win.document.selection.createRange) {
19365                 // no firefox support
19366                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19367                 win.document.selection.createRange().pasteHTML(txt);
19368             } else {
19369                 // no firefox support
19370                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19371                 this.execCmd('InsertHTML', txt);
19372             } 
19373             
19374             this.syncValue();
19375             
19376             this.deferFocus();
19377         }
19378     },
19379  // private
19380     mozKeyPress : function(e){
19381         if(e.ctrlKey){
19382             var c = e.getCharCode(), cmd;
19383           
19384             if(c > 0){
19385                 c = String.fromCharCode(c).toLowerCase();
19386                 switch(c){
19387                     case 'b':
19388                         cmd = 'bold';
19389                         break;
19390                     case 'i':
19391                         cmd = 'italic';
19392                         break;
19393                     
19394                     case 'u':
19395                         cmd = 'underline';
19396                         break;
19397                     
19398                     case 'v':
19399                         this.cleanUpPaste.defer(100, this);
19400                         return;
19401                         
19402                 }
19403                 if(cmd){
19404                     this.win.focus();
19405                     this.execCmd(cmd);
19406                     this.deferFocus();
19407                     e.preventDefault();
19408                 }
19409                 
19410             }
19411         }
19412     },
19413
19414     // private
19415     fixKeys : function(){ // load time branching for fastest keydown performance
19416         if(Roo.isIE){
19417             return function(e){
19418                 var k = e.getKey(), r;
19419                 if(k == e.TAB){
19420                     e.stopEvent();
19421                     r = this.doc.selection.createRange();
19422                     if(r){
19423                         r.collapse(true);
19424                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19425                         this.deferFocus();
19426                     }
19427                     return;
19428                 }
19429                 
19430                 if(k == e.ENTER){
19431                     r = this.doc.selection.createRange();
19432                     if(r){
19433                         var target = r.parentElement();
19434                         if(!target || target.tagName.toLowerCase() != 'li'){
19435                             e.stopEvent();
19436                             r.pasteHTML('<br />');
19437                             r.collapse(false);
19438                             r.select();
19439                         }
19440                     }
19441                 }
19442                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19443                     this.cleanUpPaste.defer(100, this);
19444                     return;
19445                 }
19446                 
19447                 
19448             };
19449         }else if(Roo.isOpera){
19450             return function(e){
19451                 var k = e.getKey();
19452                 if(k == e.TAB){
19453                     e.stopEvent();
19454                     this.win.focus();
19455                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19456                     this.deferFocus();
19457                 }
19458                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19459                     this.cleanUpPaste.defer(100, this);
19460                     return;
19461                 }
19462                 
19463             };
19464         }else if(Roo.isSafari){
19465             return function(e){
19466                 var k = e.getKey();
19467                 
19468                 if(k == e.TAB){
19469                     e.stopEvent();
19470                     this.execCmd('InsertText','\t');
19471                     this.deferFocus();
19472                     return;
19473                 }
19474                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19475                     this.cleanUpPaste.defer(100, this);
19476                     return;
19477                 }
19478                 
19479              };
19480         }
19481     }(),
19482     
19483     getAllAncestors: function()
19484     {
19485         var p = this.getSelectedNode();
19486         var a = [];
19487         if (!p) {
19488             a.push(p); // push blank onto stack..
19489             p = this.getParentElement();
19490         }
19491         
19492         
19493         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19494             a.push(p);
19495             p = p.parentNode;
19496         }
19497         a.push(this.doc.body);
19498         return a;
19499     },
19500     lastSel : false,
19501     lastSelNode : false,
19502     
19503     
19504     getSelection : function() 
19505     {
19506         this.assignDocWin();
19507         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19508     },
19509     
19510     getSelectedNode: function() 
19511     {
19512         // this may only work on Gecko!!!
19513         
19514         // should we cache this!!!!
19515         
19516         
19517         
19518          
19519         var range = this.createRange(this.getSelection()).cloneRange();
19520         
19521         if (Roo.isIE) {
19522             var parent = range.parentElement();
19523             while (true) {
19524                 var testRange = range.duplicate();
19525                 testRange.moveToElementText(parent);
19526                 if (testRange.inRange(range)) {
19527                     break;
19528                 }
19529                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19530                     break;
19531                 }
19532                 parent = parent.parentElement;
19533             }
19534             return parent;
19535         }
19536         
19537         // is ancestor a text element.
19538         var ac =  range.commonAncestorContainer;
19539         if (ac.nodeType == 3) {
19540             ac = ac.parentNode;
19541         }
19542         
19543         var ar = ac.childNodes;
19544          
19545         var nodes = [];
19546         var other_nodes = [];
19547         var has_other_nodes = false;
19548         for (var i=0;i<ar.length;i++) {
19549             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19550                 continue;
19551             }
19552             // fullly contained node.
19553             
19554             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19555                 nodes.push(ar[i]);
19556                 continue;
19557             }
19558             
19559             // probably selected..
19560             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19561                 other_nodes.push(ar[i]);
19562                 continue;
19563             }
19564             // outer..
19565             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19566                 continue;
19567             }
19568             
19569             
19570             has_other_nodes = true;
19571         }
19572         if (!nodes.length && other_nodes.length) {
19573             nodes= other_nodes;
19574         }
19575         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19576             return false;
19577         }
19578         
19579         return nodes[0];
19580     },
19581     createRange: function(sel)
19582     {
19583         // this has strange effects when using with 
19584         // top toolbar - not sure if it's a great idea.
19585         //this.editor.contentWindow.focus();
19586         if (typeof sel != "undefined") {
19587             try {
19588                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19589             } catch(e) {
19590                 return this.doc.createRange();
19591             }
19592         } else {
19593             return this.doc.createRange();
19594         }
19595     },
19596     getParentElement: function()
19597     {
19598         
19599         this.assignDocWin();
19600         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19601         
19602         var range = this.createRange(sel);
19603          
19604         try {
19605             var p = range.commonAncestorContainer;
19606             while (p.nodeType == 3) { // text node
19607                 p = p.parentNode;
19608             }
19609             return p;
19610         } catch (e) {
19611             return null;
19612         }
19613     
19614     },
19615     /***
19616      *
19617      * Range intersection.. the hard stuff...
19618      *  '-1' = before
19619      *  '0' = hits..
19620      *  '1' = after.
19621      *         [ -- selected range --- ]
19622      *   [fail]                        [fail]
19623      *
19624      *    basically..
19625      *      if end is before start or  hits it. fail.
19626      *      if start is after end or hits it fail.
19627      *
19628      *   if either hits (but other is outside. - then it's not 
19629      *   
19630      *    
19631      **/
19632     
19633     
19634     // @see http://www.thismuchiknow.co.uk/?p=64.
19635     rangeIntersectsNode : function(range, node)
19636     {
19637         var nodeRange = node.ownerDocument.createRange();
19638         try {
19639             nodeRange.selectNode(node);
19640         } catch (e) {
19641             nodeRange.selectNodeContents(node);
19642         }
19643     
19644         var rangeStartRange = range.cloneRange();
19645         rangeStartRange.collapse(true);
19646     
19647         var rangeEndRange = range.cloneRange();
19648         rangeEndRange.collapse(false);
19649     
19650         var nodeStartRange = nodeRange.cloneRange();
19651         nodeStartRange.collapse(true);
19652     
19653         var nodeEndRange = nodeRange.cloneRange();
19654         nodeEndRange.collapse(false);
19655     
19656         return rangeStartRange.compareBoundaryPoints(
19657                  Range.START_TO_START, nodeEndRange) == -1 &&
19658                rangeEndRange.compareBoundaryPoints(
19659                  Range.START_TO_START, nodeStartRange) == 1;
19660         
19661          
19662     },
19663     rangeCompareNode : function(range, node)
19664     {
19665         var nodeRange = node.ownerDocument.createRange();
19666         try {
19667             nodeRange.selectNode(node);
19668         } catch (e) {
19669             nodeRange.selectNodeContents(node);
19670         }
19671         
19672         
19673         range.collapse(true);
19674     
19675         nodeRange.collapse(true);
19676      
19677         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19678         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19679          
19680         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19681         
19682         var nodeIsBefore   =  ss == 1;
19683         var nodeIsAfter    = ee == -1;
19684         
19685         if (nodeIsBefore && nodeIsAfter)
19686             return 0; // outer
19687         if (!nodeIsBefore && nodeIsAfter)
19688             return 1; //right trailed.
19689         
19690         if (nodeIsBefore && !nodeIsAfter)
19691             return 2;  // left trailed.
19692         // fully contined.
19693         return 3;
19694     },
19695
19696     // private? - in a new class?
19697     cleanUpPaste :  function()
19698     {
19699         // cleans up the whole document..
19700         Roo.log('cleanuppaste');
19701         
19702         this.cleanUpChildren(this.doc.body);
19703         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19704         if (clean != this.doc.body.innerHTML) {
19705             this.doc.body.innerHTML = clean;
19706         }
19707         
19708     },
19709     
19710     cleanWordChars : function(input) {// change the chars to hex code
19711         var he = Roo.HtmlEditorCore;
19712         
19713         var output = input;
19714         Roo.each(he.swapCodes, function(sw) { 
19715             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19716             
19717             output = output.replace(swapper, sw[1]);
19718         });
19719         
19720         return output;
19721     },
19722     
19723     
19724     cleanUpChildren : function (n)
19725     {
19726         if (!n.childNodes.length) {
19727             return;
19728         }
19729         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19730            this.cleanUpChild(n.childNodes[i]);
19731         }
19732     },
19733     
19734     
19735         
19736     
19737     cleanUpChild : function (node)
19738     {
19739         var ed = this;
19740         //console.log(node);
19741         if (node.nodeName == "#text") {
19742             // clean up silly Windows -- stuff?
19743             return; 
19744         }
19745         if (node.nodeName == "#comment") {
19746             node.parentNode.removeChild(node);
19747             // clean up silly Windows -- stuff?
19748             return; 
19749         }
19750         var lcname = node.tagName.toLowerCase();
19751         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19752         // whitelist of tags..
19753         
19754         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19755             // remove node.
19756             node.parentNode.removeChild(node);
19757             return;
19758             
19759         }
19760         
19761         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19762         
19763         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19764         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19765         
19766         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19767         //    remove_keep_children = true;
19768         //}
19769         
19770         if (remove_keep_children) {
19771             this.cleanUpChildren(node);
19772             // inserts everything just before this node...
19773             while (node.childNodes.length) {
19774                 var cn = node.childNodes[0];
19775                 node.removeChild(cn);
19776                 node.parentNode.insertBefore(cn, node);
19777             }
19778             node.parentNode.removeChild(node);
19779             return;
19780         }
19781         
19782         if (!node.attributes || !node.attributes.length) {
19783             this.cleanUpChildren(node);
19784             return;
19785         }
19786         
19787         function cleanAttr(n,v)
19788         {
19789             
19790             if (v.match(/^\./) || v.match(/^\//)) {
19791                 return;
19792             }
19793             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19794                 return;
19795             }
19796             if (v.match(/^#/)) {
19797                 return;
19798             }
19799 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19800             node.removeAttribute(n);
19801             
19802         }
19803         
19804         var cwhite = this.cwhite;
19805         var cblack = this.cblack;
19806             
19807         function cleanStyle(n,v)
19808         {
19809             if (v.match(/expression/)) { //XSS?? should we even bother..
19810                 node.removeAttribute(n);
19811                 return;
19812             }
19813             
19814             var parts = v.split(/;/);
19815             var clean = [];
19816             
19817             Roo.each(parts, function(p) {
19818                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19819                 if (!p.length) {
19820                     return true;
19821                 }
19822                 var l = p.split(':').shift().replace(/\s+/g,'');
19823                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19824                 
19825                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19826 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19827                     //node.removeAttribute(n);
19828                     return true;
19829                 }
19830                 //Roo.log()
19831                 // only allow 'c whitelisted system attributes'
19832                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19833 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19834                     //node.removeAttribute(n);
19835                     return true;
19836                 }
19837                 
19838                 
19839                  
19840                 
19841                 clean.push(p);
19842                 return true;
19843             });
19844             if (clean.length) { 
19845                 node.setAttribute(n, clean.join(';'));
19846             } else {
19847                 node.removeAttribute(n);
19848             }
19849             
19850         }
19851         
19852         
19853         for (var i = node.attributes.length-1; i > -1 ; i--) {
19854             var a = node.attributes[i];
19855             //console.log(a);
19856             
19857             if (a.name.toLowerCase().substr(0,2)=='on')  {
19858                 node.removeAttribute(a.name);
19859                 continue;
19860             }
19861             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19862                 node.removeAttribute(a.name);
19863                 continue;
19864             }
19865             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19866                 cleanAttr(a.name,a.value); // fixme..
19867                 continue;
19868             }
19869             if (a.name == 'style') {
19870                 cleanStyle(a.name,a.value);
19871                 continue;
19872             }
19873             /// clean up MS crap..
19874             // tecnically this should be a list of valid class'es..
19875             
19876             
19877             if (a.name == 'class') {
19878                 if (a.value.match(/^Mso/)) {
19879                     node.className = '';
19880                 }
19881                 
19882                 if (a.value.match(/body/)) {
19883                     node.className = '';
19884                 }
19885                 continue;
19886             }
19887             
19888             // style cleanup!?
19889             // class cleanup?
19890             
19891         }
19892         
19893         
19894         this.cleanUpChildren(node);
19895         
19896         
19897     },
19898     
19899     /**
19900      * Clean up MS wordisms...
19901      */
19902     cleanWord : function(node)
19903     {
19904         
19905         
19906         if (!node) {
19907             this.cleanWord(this.doc.body);
19908             return;
19909         }
19910         if (node.nodeName == "#text") {
19911             // clean up silly Windows -- stuff?
19912             return; 
19913         }
19914         if (node.nodeName == "#comment") {
19915             node.parentNode.removeChild(node);
19916             // clean up silly Windows -- stuff?
19917             return; 
19918         }
19919         
19920         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19921             node.parentNode.removeChild(node);
19922             return;
19923         }
19924         
19925         // remove - but keep children..
19926         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19927             while (node.childNodes.length) {
19928                 var cn = node.childNodes[0];
19929                 node.removeChild(cn);
19930                 node.parentNode.insertBefore(cn, node);
19931             }
19932             node.parentNode.removeChild(node);
19933             this.iterateChildren(node, this.cleanWord);
19934             return;
19935         }
19936         // clean styles
19937         if (node.className.length) {
19938             
19939             var cn = node.className.split(/\W+/);
19940             var cna = [];
19941             Roo.each(cn, function(cls) {
19942                 if (cls.match(/Mso[a-zA-Z]+/)) {
19943                     return;
19944                 }
19945                 cna.push(cls);
19946             });
19947             node.className = cna.length ? cna.join(' ') : '';
19948             if (!cna.length) {
19949                 node.removeAttribute("class");
19950             }
19951         }
19952         
19953         if (node.hasAttribute("lang")) {
19954             node.removeAttribute("lang");
19955         }
19956         
19957         if (node.hasAttribute("style")) {
19958             
19959             var styles = node.getAttribute("style").split(";");
19960             var nstyle = [];
19961             Roo.each(styles, function(s) {
19962                 if (!s.match(/:/)) {
19963                     return;
19964                 }
19965                 var kv = s.split(":");
19966                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19967                     return;
19968                 }
19969                 // what ever is left... we allow.
19970                 nstyle.push(s);
19971             });
19972             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19973             if (!nstyle.length) {
19974                 node.removeAttribute('style');
19975             }
19976         }
19977         this.iterateChildren(node, this.cleanWord);
19978         
19979         
19980         
19981     },
19982     /**
19983      * iterateChildren of a Node, calling fn each time, using this as the scole..
19984      * @param {DomNode} node node to iterate children of.
19985      * @param {Function} fn method of this class to call on each item.
19986      */
19987     iterateChildren : function(node, fn)
19988     {
19989         if (!node.childNodes.length) {
19990                 return;
19991         }
19992         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19993            fn.call(this, node.childNodes[i])
19994         }
19995     },
19996     
19997     
19998     /**
19999      * cleanTableWidths.
20000      *
20001      * Quite often pasting from word etc.. results in tables with column and widths.
20002      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20003      *
20004      */
20005     cleanTableWidths : function(node)
20006     {
20007          
20008          
20009         if (!node) {
20010             this.cleanTableWidths(this.doc.body);
20011             return;
20012         }
20013         
20014         // ignore list...
20015         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20016             return; 
20017         }
20018         Roo.log(node.tagName);
20019         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20020             this.iterateChildren(node, this.cleanTableWidths);
20021             return;
20022         }
20023         if (node.hasAttribute('width')) {
20024             node.removeAttribute('width');
20025         }
20026         
20027          
20028         if (node.hasAttribute("style")) {
20029             // pretty basic...
20030             
20031             var styles = node.getAttribute("style").split(";");
20032             var nstyle = [];
20033             Roo.each(styles, function(s) {
20034                 if (!s.match(/:/)) {
20035                     return;
20036                 }
20037                 var kv = s.split(":");
20038                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20039                     return;
20040                 }
20041                 // what ever is left... we allow.
20042                 nstyle.push(s);
20043             });
20044             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20045             if (!nstyle.length) {
20046                 node.removeAttribute('style');
20047             }
20048         }
20049         
20050         this.iterateChildren(node, this.cleanTableWidths);
20051         
20052         
20053     },
20054     
20055     
20056     
20057     
20058     domToHTML : function(currentElement, depth, nopadtext) {
20059         
20060         depth = depth || 0;
20061         nopadtext = nopadtext || false;
20062     
20063         if (!currentElement) {
20064             return this.domToHTML(this.doc.body);
20065         }
20066         
20067         //Roo.log(currentElement);
20068         var j;
20069         var allText = false;
20070         var nodeName = currentElement.nodeName;
20071         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20072         
20073         if  (nodeName == '#text') {
20074             
20075             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20076         }
20077         
20078         
20079         var ret = '';
20080         if (nodeName != 'BODY') {
20081              
20082             var i = 0;
20083             // Prints the node tagName, such as <A>, <IMG>, etc
20084             if (tagName) {
20085                 var attr = [];
20086                 for(i = 0; i < currentElement.attributes.length;i++) {
20087                     // quoting?
20088                     var aname = currentElement.attributes.item(i).name;
20089                     if (!currentElement.attributes.item(i).value.length) {
20090                         continue;
20091                     }
20092                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20093                 }
20094                 
20095                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20096             } 
20097             else {
20098                 
20099                 // eack
20100             }
20101         } else {
20102             tagName = false;
20103         }
20104         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20105             return ret;
20106         }
20107         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20108             nopadtext = true;
20109         }
20110         
20111         
20112         // Traverse the tree
20113         i = 0;
20114         var currentElementChild = currentElement.childNodes.item(i);
20115         var allText = true;
20116         var innerHTML  = '';
20117         lastnode = '';
20118         while (currentElementChild) {
20119             // Formatting code (indent the tree so it looks nice on the screen)
20120             var nopad = nopadtext;
20121             if (lastnode == 'SPAN') {
20122                 nopad  = true;
20123             }
20124             // text
20125             if  (currentElementChild.nodeName == '#text') {
20126                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20127                 toadd = nopadtext ? toadd : toadd.trim();
20128                 if (!nopad && toadd.length > 80) {
20129                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20130                 }
20131                 innerHTML  += toadd;
20132                 
20133                 i++;
20134                 currentElementChild = currentElement.childNodes.item(i);
20135                 lastNode = '';
20136                 continue;
20137             }
20138             allText = false;
20139             
20140             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20141                 
20142             // Recursively traverse the tree structure of the child node
20143             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20144             lastnode = currentElementChild.nodeName;
20145             i++;
20146             currentElementChild=currentElement.childNodes.item(i);
20147         }
20148         
20149         ret += innerHTML;
20150         
20151         if (!allText) {
20152                 // The remaining code is mostly for formatting the tree
20153             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20154         }
20155         
20156         
20157         if (tagName) {
20158             ret+= "</"+tagName+">";
20159         }
20160         return ret;
20161         
20162     },
20163         
20164     applyBlacklists : function()
20165     {
20166         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20167         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20168         
20169         this.white = [];
20170         this.black = [];
20171         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20172             if (b.indexOf(tag) > -1) {
20173                 return;
20174             }
20175             this.white.push(tag);
20176             
20177         }, this);
20178         
20179         Roo.each(w, function(tag) {
20180             if (b.indexOf(tag) > -1) {
20181                 return;
20182             }
20183             if (this.white.indexOf(tag) > -1) {
20184                 return;
20185             }
20186             this.white.push(tag);
20187             
20188         }, this);
20189         
20190         
20191         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20192             if (w.indexOf(tag) > -1) {
20193                 return;
20194             }
20195             this.black.push(tag);
20196             
20197         }, this);
20198         
20199         Roo.each(b, function(tag) {
20200             if (w.indexOf(tag) > -1) {
20201                 return;
20202             }
20203             if (this.black.indexOf(tag) > -1) {
20204                 return;
20205             }
20206             this.black.push(tag);
20207             
20208         }, this);
20209         
20210         
20211         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20212         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20213         
20214         this.cwhite = [];
20215         this.cblack = [];
20216         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20217             if (b.indexOf(tag) > -1) {
20218                 return;
20219             }
20220             this.cwhite.push(tag);
20221             
20222         }, this);
20223         
20224         Roo.each(w, function(tag) {
20225             if (b.indexOf(tag) > -1) {
20226                 return;
20227             }
20228             if (this.cwhite.indexOf(tag) > -1) {
20229                 return;
20230             }
20231             this.cwhite.push(tag);
20232             
20233         }, this);
20234         
20235         
20236         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20237             if (w.indexOf(tag) > -1) {
20238                 return;
20239             }
20240             this.cblack.push(tag);
20241             
20242         }, this);
20243         
20244         Roo.each(b, function(tag) {
20245             if (w.indexOf(tag) > -1) {
20246                 return;
20247             }
20248             if (this.cblack.indexOf(tag) > -1) {
20249                 return;
20250             }
20251             this.cblack.push(tag);
20252             
20253         }, this);
20254     },
20255     
20256     setStylesheets : function(stylesheets)
20257     {
20258         if(typeof(stylesheets) == 'string'){
20259             Roo.get(this.iframe.contentDocument.head).createChild({
20260                 tag : 'link',
20261                 rel : 'stylesheet',
20262                 type : 'text/css',
20263                 href : stylesheets
20264             });
20265             
20266             return;
20267         }
20268         var _this = this;
20269      
20270         Roo.each(stylesheets, function(s) {
20271             if(!s.length){
20272                 return;
20273             }
20274             
20275             Roo.get(_this.iframe.contentDocument.head).createChild({
20276                 tag : 'link',
20277                 rel : 'stylesheet',
20278                 type : 'text/css',
20279                 href : s
20280             });
20281         });
20282
20283         
20284     },
20285     
20286     removeStylesheets : function()
20287     {
20288         var _this = this;
20289         
20290         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20291             s.remove();
20292         });
20293     }
20294     
20295     // hide stuff that is not compatible
20296     /**
20297      * @event blur
20298      * @hide
20299      */
20300     /**
20301      * @event change
20302      * @hide
20303      */
20304     /**
20305      * @event focus
20306      * @hide
20307      */
20308     /**
20309      * @event specialkey
20310      * @hide
20311      */
20312     /**
20313      * @cfg {String} fieldClass @hide
20314      */
20315     /**
20316      * @cfg {String} focusClass @hide
20317      */
20318     /**
20319      * @cfg {String} autoCreate @hide
20320      */
20321     /**
20322      * @cfg {String} inputType @hide
20323      */
20324     /**
20325      * @cfg {String} invalidClass @hide
20326      */
20327     /**
20328      * @cfg {String} invalidText @hide
20329      */
20330     /**
20331      * @cfg {String} msgFx @hide
20332      */
20333     /**
20334      * @cfg {String} validateOnBlur @hide
20335      */
20336 });
20337
20338 Roo.HtmlEditorCore.white = [
20339         'area', 'br', 'img', 'input', 'hr', 'wbr',
20340         
20341        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20342        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20343        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20344        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20345        'table',   'ul',         'xmp', 
20346        
20347        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20348       'thead',   'tr', 
20349      
20350       'dir', 'menu', 'ol', 'ul', 'dl',
20351        
20352       'embed',  'object'
20353 ];
20354
20355
20356 Roo.HtmlEditorCore.black = [
20357     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20358         'applet', // 
20359         'base',   'basefont', 'bgsound', 'blink',  'body', 
20360         'frame',  'frameset', 'head',    'html',   'ilayer', 
20361         'iframe', 'layer',  'link',     'meta',    'object',   
20362         'script', 'style' ,'title',  'xml' // clean later..
20363 ];
20364 Roo.HtmlEditorCore.clean = [
20365     'script', 'style', 'title', 'xml'
20366 ];
20367 Roo.HtmlEditorCore.remove = [
20368     'font'
20369 ];
20370 // attributes..
20371
20372 Roo.HtmlEditorCore.ablack = [
20373     'on'
20374 ];
20375     
20376 Roo.HtmlEditorCore.aclean = [ 
20377     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20378 ];
20379
20380 // protocols..
20381 Roo.HtmlEditorCore.pwhite= [
20382         'http',  'https',  'mailto'
20383 ];
20384
20385 // white listed style attributes.
20386 Roo.HtmlEditorCore.cwhite= [
20387       //  'text-align', /// default is to allow most things..
20388       
20389          
20390 //        'font-size'//??
20391 ];
20392
20393 // black listed style attributes.
20394 Roo.HtmlEditorCore.cblack= [
20395       //  'font-size' -- this can be set by the project 
20396 ];
20397
20398
20399 Roo.HtmlEditorCore.swapCodes   =[ 
20400     [    8211, "--" ], 
20401     [    8212, "--" ], 
20402     [    8216,  "'" ],  
20403     [    8217, "'" ],  
20404     [    8220, '"' ],  
20405     [    8221, '"' ],  
20406     [    8226, "*" ],  
20407     [    8230, "..." ]
20408 ]; 
20409
20410     /*
20411  * - LGPL
20412  *
20413  * HtmlEditor
20414  * 
20415  */
20416
20417 /**
20418  * @class Roo.bootstrap.HtmlEditor
20419  * @extends Roo.bootstrap.TextArea
20420  * Bootstrap HtmlEditor class
20421
20422  * @constructor
20423  * Create a new HtmlEditor
20424  * @param {Object} config The config object
20425  */
20426
20427 Roo.bootstrap.HtmlEditor = function(config){
20428     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20429     if (!this.toolbars) {
20430         this.toolbars = [];
20431     }
20432     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20433     this.addEvents({
20434             /**
20435              * @event initialize
20436              * Fires when the editor is fully initialized (including the iframe)
20437              * @param {HtmlEditor} this
20438              */
20439             initialize: true,
20440             /**
20441              * @event activate
20442              * Fires when the editor is first receives the focus. Any insertion must wait
20443              * until after this event.
20444              * @param {HtmlEditor} this
20445              */
20446             activate: true,
20447              /**
20448              * @event beforesync
20449              * Fires before the textarea is updated with content from the editor iframe. Return false
20450              * to cancel the sync.
20451              * @param {HtmlEditor} this
20452              * @param {String} html
20453              */
20454             beforesync: true,
20455              /**
20456              * @event beforepush
20457              * Fires before the iframe editor is updated with content from the textarea. Return false
20458              * to cancel the push.
20459              * @param {HtmlEditor} this
20460              * @param {String} html
20461              */
20462             beforepush: true,
20463              /**
20464              * @event sync
20465              * Fires when the textarea is updated with content from the editor iframe.
20466              * @param {HtmlEditor} this
20467              * @param {String} html
20468              */
20469             sync: true,
20470              /**
20471              * @event push
20472              * Fires when the iframe editor is updated with content from the textarea.
20473              * @param {HtmlEditor} this
20474              * @param {String} html
20475              */
20476             push: true,
20477              /**
20478              * @event editmodechange
20479              * Fires when the editor switches edit modes
20480              * @param {HtmlEditor} this
20481              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20482              */
20483             editmodechange: true,
20484             /**
20485              * @event editorevent
20486              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20487              * @param {HtmlEditor} this
20488              */
20489             editorevent: true,
20490             /**
20491              * @event firstfocus
20492              * Fires when on first focus - needed by toolbars..
20493              * @param {HtmlEditor} this
20494              */
20495             firstfocus: true,
20496             /**
20497              * @event autosave
20498              * Auto save the htmlEditor value as a file into Events
20499              * @param {HtmlEditor} this
20500              */
20501             autosave: true,
20502             /**
20503              * @event savedpreview
20504              * preview the saved version of htmlEditor
20505              * @param {HtmlEditor} this
20506              */
20507             savedpreview: true
20508         });
20509 };
20510
20511
20512 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20513     
20514     
20515       /**
20516      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20517      */
20518     toolbars : false,
20519    
20520      /**
20521      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20522      *                        Roo.resizable.
20523      */
20524     resizable : false,
20525      /**
20526      * @cfg {Number} height (in pixels)
20527      */   
20528     height: 300,
20529    /**
20530      * @cfg {Number} width (in pixels)
20531      */   
20532     width: false,
20533     
20534     /**
20535      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20536      * 
20537      */
20538     stylesheets: false,
20539     
20540     // id of frame..
20541     frameId: false,
20542     
20543     // private properties
20544     validationEvent : false,
20545     deferHeight: true,
20546     initialized : false,
20547     activated : false,
20548     
20549     onFocus : Roo.emptyFn,
20550     iframePad:3,
20551     hideMode:'offsets',
20552     
20553     
20554     tbContainer : false,
20555     
20556     toolbarContainer :function() {
20557         return this.wrap.select('.x-html-editor-tb',true).first();
20558     },
20559
20560     /**
20561      * Protected method that will not generally be called directly. It
20562      * is called when the editor creates its toolbar. Override this method if you need to
20563      * add custom toolbar buttons.
20564      * @param {HtmlEditor} editor
20565      */
20566     createToolbar : function(){
20567         
20568         Roo.log("create toolbars");
20569         
20570         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20571         this.toolbars[0].render(this.toolbarContainer());
20572         
20573         return;
20574         
20575 //        if (!editor.toolbars || !editor.toolbars.length) {
20576 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20577 //        }
20578 //        
20579 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20580 //            editor.toolbars[i] = Roo.factory(
20581 //                    typeof(editor.toolbars[i]) == 'string' ?
20582 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20583 //                Roo.bootstrap.HtmlEditor);
20584 //            editor.toolbars[i].init(editor);
20585 //        }
20586     },
20587
20588      
20589     // private
20590     onRender : function(ct, position)
20591     {
20592        // Roo.log("Call onRender: " + this.xtype);
20593         var _t = this;
20594         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20595       
20596         this.wrap = this.inputEl().wrap({
20597             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20598         });
20599         
20600         this.editorcore.onRender(ct, position);
20601          
20602         if (this.resizable) {
20603             this.resizeEl = new Roo.Resizable(this.wrap, {
20604                 pinned : true,
20605                 wrap: true,
20606                 dynamic : true,
20607                 minHeight : this.height,
20608                 height: this.height,
20609                 handles : this.resizable,
20610                 width: this.width,
20611                 listeners : {
20612                     resize : function(r, w, h) {
20613                         _t.onResize(w,h); // -something
20614                     }
20615                 }
20616             });
20617             
20618         }
20619         this.createToolbar(this);
20620        
20621         
20622         if(!this.width && this.resizable){
20623             this.setSize(this.wrap.getSize());
20624         }
20625         if (this.resizeEl) {
20626             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20627             // should trigger onReize..
20628         }
20629         
20630     },
20631
20632     // private
20633     onResize : function(w, h)
20634     {
20635         Roo.log('resize: ' +w + ',' + h );
20636         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20637         var ew = false;
20638         var eh = false;
20639         
20640         if(this.inputEl() ){
20641             if(typeof w == 'number'){
20642                 var aw = w - this.wrap.getFrameWidth('lr');
20643                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20644                 ew = aw;
20645             }
20646             if(typeof h == 'number'){
20647                  var tbh = -11;  // fixme it needs to tool bar size!
20648                 for (var i =0; i < this.toolbars.length;i++) {
20649                     // fixme - ask toolbars for heights?
20650                     tbh += this.toolbars[i].el.getHeight();
20651                     //if (this.toolbars[i].footer) {
20652                     //    tbh += this.toolbars[i].footer.el.getHeight();
20653                     //}
20654                 }
20655               
20656                 
20657                 
20658                 
20659                 
20660                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20661                 ah -= 5; // knock a few pixes off for look..
20662                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20663                 var eh = ah;
20664             }
20665         }
20666         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20667         this.editorcore.onResize(ew,eh);
20668         
20669     },
20670
20671     /**
20672      * Toggles the editor between standard and source edit mode.
20673      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20674      */
20675     toggleSourceEdit : function(sourceEditMode)
20676     {
20677         this.editorcore.toggleSourceEdit(sourceEditMode);
20678         
20679         if(this.editorcore.sourceEditMode){
20680             Roo.log('editor - showing textarea');
20681             
20682 //            Roo.log('in');
20683 //            Roo.log(this.syncValue());
20684             this.syncValue();
20685             this.inputEl().removeClass(['hide', 'x-hidden']);
20686             this.inputEl().dom.removeAttribute('tabIndex');
20687             this.inputEl().focus();
20688         }else{
20689             Roo.log('editor - hiding textarea');
20690 //            Roo.log('out')
20691 //            Roo.log(this.pushValue()); 
20692             this.pushValue();
20693             
20694             this.inputEl().addClass(['hide', 'x-hidden']);
20695             this.inputEl().dom.setAttribute('tabIndex', -1);
20696             //this.deferFocus();
20697         }
20698          
20699         if(this.resizable){
20700             this.setSize(this.wrap.getSize());
20701         }
20702         
20703         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20704     },
20705  
20706     // private (for BoxComponent)
20707     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20708
20709     // private (for BoxComponent)
20710     getResizeEl : function(){
20711         return this.wrap;
20712     },
20713
20714     // private (for BoxComponent)
20715     getPositionEl : function(){
20716         return this.wrap;
20717     },
20718
20719     // private
20720     initEvents : function(){
20721         this.originalValue = this.getValue();
20722     },
20723
20724 //    /**
20725 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20726 //     * @method
20727 //     */
20728 //    markInvalid : Roo.emptyFn,
20729 //    /**
20730 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20731 //     * @method
20732 //     */
20733 //    clearInvalid : Roo.emptyFn,
20734
20735     setValue : function(v){
20736         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20737         this.editorcore.pushValue();
20738     },
20739
20740      
20741     // private
20742     deferFocus : function(){
20743         this.focus.defer(10, this);
20744     },
20745
20746     // doc'ed in Field
20747     focus : function(){
20748         this.editorcore.focus();
20749         
20750     },
20751       
20752
20753     // private
20754     onDestroy : function(){
20755         
20756         
20757         
20758         if(this.rendered){
20759             
20760             for (var i =0; i < this.toolbars.length;i++) {
20761                 // fixme - ask toolbars for heights?
20762                 this.toolbars[i].onDestroy();
20763             }
20764             
20765             this.wrap.dom.innerHTML = '';
20766             this.wrap.remove();
20767         }
20768     },
20769
20770     // private
20771     onFirstFocus : function(){
20772         //Roo.log("onFirstFocus");
20773         this.editorcore.onFirstFocus();
20774          for (var i =0; i < this.toolbars.length;i++) {
20775             this.toolbars[i].onFirstFocus();
20776         }
20777         
20778     },
20779     
20780     // private
20781     syncValue : function()
20782     {   
20783         this.editorcore.syncValue();
20784     },
20785     
20786     pushValue : function()
20787     {   
20788         this.editorcore.pushValue();
20789     }
20790      
20791     
20792     // hide stuff that is not compatible
20793     /**
20794      * @event blur
20795      * @hide
20796      */
20797     /**
20798      * @event change
20799      * @hide
20800      */
20801     /**
20802      * @event focus
20803      * @hide
20804      */
20805     /**
20806      * @event specialkey
20807      * @hide
20808      */
20809     /**
20810      * @cfg {String} fieldClass @hide
20811      */
20812     /**
20813      * @cfg {String} focusClass @hide
20814      */
20815     /**
20816      * @cfg {String} autoCreate @hide
20817      */
20818     /**
20819      * @cfg {String} inputType @hide
20820      */
20821     /**
20822      * @cfg {String} invalidClass @hide
20823      */
20824     /**
20825      * @cfg {String} invalidText @hide
20826      */
20827     /**
20828      * @cfg {String} msgFx @hide
20829      */
20830     /**
20831      * @cfg {String} validateOnBlur @hide
20832      */
20833 });
20834  
20835     
20836    
20837    
20838    
20839       
20840 Roo.namespace('Roo.bootstrap.htmleditor');
20841 /**
20842  * @class Roo.bootstrap.HtmlEditorToolbar1
20843  * Basic Toolbar
20844  * 
20845  * Usage:
20846  *
20847  new Roo.bootstrap.HtmlEditor({
20848     ....
20849     toolbars : [
20850         new Roo.bootstrap.HtmlEditorToolbar1({
20851             disable : { fonts: 1 , format: 1, ..., ... , ...],
20852             btns : [ .... ]
20853         })
20854     }
20855      
20856  * 
20857  * @cfg {Object} disable List of elements to disable..
20858  * @cfg {Array} btns List of additional buttons.
20859  * 
20860  * 
20861  * NEEDS Extra CSS? 
20862  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20863  */
20864  
20865 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20866 {
20867     
20868     Roo.apply(this, config);
20869     
20870     // default disabled, based on 'good practice'..
20871     this.disable = this.disable || {};
20872     Roo.applyIf(this.disable, {
20873         fontSize : true,
20874         colors : true,
20875         specialElements : true
20876     });
20877     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20878     
20879     this.editor = config.editor;
20880     this.editorcore = config.editor.editorcore;
20881     
20882     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20883     
20884     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20885     // dont call parent... till later.
20886 }
20887 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20888      
20889     bar : true,
20890     
20891     editor : false,
20892     editorcore : false,
20893     
20894     
20895     formats : [
20896         "p" ,  
20897         "h1","h2","h3","h4","h5","h6", 
20898         "pre", "code", 
20899         "abbr", "acronym", "address", "cite", "samp", "var",
20900         'div','span'
20901     ],
20902     
20903     onRender : function(ct, position)
20904     {
20905        // Roo.log("Call onRender: " + this.xtype);
20906         
20907        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20908        Roo.log(this.el);
20909        this.el.dom.style.marginBottom = '0';
20910        var _this = this;
20911        var editorcore = this.editorcore;
20912        var editor= this.editor;
20913        
20914        var children = [];
20915        var btn = function(id,cmd , toggle, handler){
20916        
20917             var  event = toggle ? 'toggle' : 'click';
20918        
20919             var a = {
20920                 size : 'sm',
20921                 xtype: 'Button',
20922                 xns: Roo.bootstrap,
20923                 glyphicon : id,
20924                 cmd : id || cmd,
20925                 enableToggle:toggle !== false,
20926                 //html : 'submit'
20927                 pressed : toggle ? false : null,
20928                 listeners : {}
20929             };
20930             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20931                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20932             };
20933             children.push(a);
20934             return a;
20935        }
20936         
20937         var style = {
20938                 xtype: 'Button',
20939                 size : 'sm',
20940                 xns: Roo.bootstrap,
20941                 glyphicon : 'font',
20942                 //html : 'submit'
20943                 menu : {
20944                     xtype: 'Menu',
20945                     xns: Roo.bootstrap,
20946                     items:  []
20947                 }
20948         };
20949         Roo.each(this.formats, function(f) {
20950             style.menu.items.push({
20951                 xtype :'MenuItem',
20952                 xns: Roo.bootstrap,
20953                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20954                 tagname : f,
20955                 listeners : {
20956                     click : function()
20957                     {
20958                         editorcore.insertTag(this.tagname);
20959                         editor.focus();
20960                     }
20961                 }
20962                 
20963             });
20964         });
20965          children.push(style);   
20966             
20967             
20968         btn('bold',false,true);
20969         btn('italic',false,true);
20970         btn('align-left', 'justifyleft',true);
20971         btn('align-center', 'justifycenter',true);
20972         btn('align-right' , 'justifyright',true);
20973         btn('link', false, false, function(btn) {
20974             //Roo.log("create link?");
20975             var url = prompt(this.createLinkText, this.defaultLinkValue);
20976             if(url && url != 'http:/'+'/'){
20977                 this.editorcore.relayCmd('createlink', url);
20978             }
20979         }),
20980         btn('list','insertunorderedlist',true);
20981         btn('pencil', false,true, function(btn){
20982                 Roo.log(this);
20983                 
20984                 this.toggleSourceEdit(btn.pressed);
20985         });
20986         /*
20987         var cog = {
20988                 xtype: 'Button',
20989                 size : 'sm',
20990                 xns: Roo.bootstrap,
20991                 glyphicon : 'cog',
20992                 //html : 'submit'
20993                 menu : {
20994                     xtype: 'Menu',
20995                     xns: Roo.bootstrap,
20996                     items:  []
20997                 }
20998         };
20999         
21000         cog.menu.items.push({
21001             xtype :'MenuItem',
21002             xns: Roo.bootstrap,
21003             html : Clean styles,
21004             tagname : f,
21005             listeners : {
21006                 click : function()
21007                 {
21008                     editorcore.insertTag(this.tagname);
21009                     editor.focus();
21010                 }
21011             }
21012             
21013         });
21014        */
21015         
21016          
21017        this.xtype = 'NavSimplebar';
21018         
21019         for(var i=0;i< children.length;i++) {
21020             
21021             this.buttons.add(this.addxtypeChild(children[i]));
21022             
21023         }
21024         
21025         editor.on('editorevent', this.updateToolbar, this);
21026     },
21027     onBtnClick : function(id)
21028     {
21029        this.editorcore.relayCmd(id);
21030        this.editorcore.focus();
21031     },
21032     
21033     /**
21034      * Protected method that will not generally be called directly. It triggers
21035      * a toolbar update by reading the markup state of the current selection in the editor.
21036      */
21037     updateToolbar: function(){
21038
21039         if(!this.editorcore.activated){
21040             this.editor.onFirstFocus(); // is this neeed?
21041             return;
21042         }
21043
21044         var btns = this.buttons; 
21045         var doc = this.editorcore.doc;
21046         btns.get('bold').setActive(doc.queryCommandState('bold'));
21047         btns.get('italic').setActive(doc.queryCommandState('italic'));
21048         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21049         
21050         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21051         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21052         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21053         
21054         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21055         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21056          /*
21057         
21058         var ans = this.editorcore.getAllAncestors();
21059         if (this.formatCombo) {
21060             
21061             
21062             var store = this.formatCombo.store;
21063             this.formatCombo.setValue("");
21064             for (var i =0; i < ans.length;i++) {
21065                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21066                     // select it..
21067                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21068                     break;
21069                 }
21070             }
21071         }
21072         
21073         
21074         
21075         // hides menus... - so this cant be on a menu...
21076         Roo.bootstrap.MenuMgr.hideAll();
21077         */
21078         Roo.bootstrap.MenuMgr.hideAll();
21079         //this.editorsyncValue();
21080     },
21081     onFirstFocus: function() {
21082         this.buttons.each(function(item){
21083            item.enable();
21084         });
21085     },
21086     toggleSourceEdit : function(sourceEditMode){
21087         
21088           
21089         if(sourceEditMode){
21090             Roo.log("disabling buttons");
21091            this.buttons.each( function(item){
21092                 if(item.cmd != 'pencil'){
21093                     item.disable();
21094                 }
21095             });
21096           
21097         }else{
21098             Roo.log("enabling buttons");
21099             if(this.editorcore.initialized){
21100                 this.buttons.each( function(item){
21101                     item.enable();
21102                 });
21103             }
21104             
21105         }
21106         Roo.log("calling toggole on editor");
21107         // tell the editor that it's been pressed..
21108         this.editor.toggleSourceEdit(sourceEditMode);
21109        
21110     }
21111 });
21112
21113
21114
21115
21116
21117 /**
21118  * @class Roo.bootstrap.Table.AbstractSelectionModel
21119  * @extends Roo.util.Observable
21120  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21121  * implemented by descendant classes.  This class should not be directly instantiated.
21122  * @constructor
21123  */
21124 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21125     this.locked = false;
21126     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21127 };
21128
21129
21130 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21131     /** @ignore Called by the grid automatically. Do not call directly. */
21132     init : function(grid){
21133         this.grid = grid;
21134         this.initEvents();
21135     },
21136
21137     /**
21138      * Locks the selections.
21139      */
21140     lock : function(){
21141         this.locked = true;
21142     },
21143
21144     /**
21145      * Unlocks the selections.
21146      */
21147     unlock : function(){
21148         this.locked = false;
21149     },
21150
21151     /**
21152      * Returns true if the selections are locked.
21153      * @return {Boolean}
21154      */
21155     isLocked : function(){
21156         return this.locked;
21157     }
21158 });
21159 /**
21160  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21161  * @class Roo.bootstrap.Table.RowSelectionModel
21162  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21163  * It supports multiple selections and keyboard selection/navigation. 
21164  * @constructor
21165  * @param {Object} config
21166  */
21167
21168 Roo.bootstrap.Table.RowSelectionModel = function(config){
21169     Roo.apply(this, config);
21170     this.selections = new Roo.util.MixedCollection(false, function(o){
21171         return o.id;
21172     });
21173
21174     this.last = false;
21175     this.lastActive = false;
21176
21177     this.addEvents({
21178         /**
21179              * @event selectionchange
21180              * Fires when the selection changes
21181              * @param {SelectionModel} this
21182              */
21183             "selectionchange" : true,
21184         /**
21185              * @event afterselectionchange
21186              * Fires after the selection changes (eg. by key press or clicking)
21187              * @param {SelectionModel} this
21188              */
21189             "afterselectionchange" : true,
21190         /**
21191              * @event beforerowselect
21192              * Fires when a row is selected being selected, return false to cancel.
21193              * @param {SelectionModel} this
21194              * @param {Number} rowIndex The selected index
21195              * @param {Boolean} keepExisting False if other selections will be cleared
21196              */
21197             "beforerowselect" : true,
21198         /**
21199              * @event rowselect
21200              * Fires when a row is selected.
21201              * @param {SelectionModel} this
21202              * @param {Number} rowIndex The selected index
21203              * @param {Roo.data.Record} r The record
21204              */
21205             "rowselect" : true,
21206         /**
21207              * @event rowdeselect
21208              * Fires when a row is deselected.
21209              * @param {SelectionModel} this
21210              * @param {Number} rowIndex The selected index
21211              */
21212         "rowdeselect" : true
21213     });
21214     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21215     this.locked = false;
21216 };
21217
21218 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21219     /**
21220      * @cfg {Boolean} singleSelect
21221      * True to allow selection of only one row at a time (defaults to false)
21222      */
21223     singleSelect : false,
21224
21225     // private
21226     initEvents : function(){
21227
21228         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21229             this.grid.on("mousedown", this.handleMouseDown, this);
21230         }else{ // allow click to work like normal
21231             this.grid.on("rowclick", this.handleDragableRowClick, this);
21232         }
21233
21234         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21235             "up" : function(e){
21236                 if(!e.shiftKey){
21237                     this.selectPrevious(e.shiftKey);
21238                 }else if(this.last !== false && this.lastActive !== false){
21239                     var last = this.last;
21240                     this.selectRange(this.last,  this.lastActive-1);
21241                     this.grid.getView().focusRow(this.lastActive);
21242                     if(last !== false){
21243                         this.last = last;
21244                     }
21245                 }else{
21246                     this.selectFirstRow();
21247                 }
21248                 this.fireEvent("afterselectionchange", this);
21249             },
21250             "down" : function(e){
21251                 if(!e.shiftKey){
21252                     this.selectNext(e.shiftKey);
21253                 }else if(this.last !== false && this.lastActive !== false){
21254                     var last = this.last;
21255                     this.selectRange(this.last,  this.lastActive+1);
21256                     this.grid.getView().focusRow(this.lastActive);
21257                     if(last !== false){
21258                         this.last = last;
21259                     }
21260                 }else{
21261                     this.selectFirstRow();
21262                 }
21263                 this.fireEvent("afterselectionchange", this);
21264             },
21265             scope: this
21266         });
21267
21268         var view = this.grid.view;
21269         view.on("refresh", this.onRefresh, this);
21270         view.on("rowupdated", this.onRowUpdated, this);
21271         view.on("rowremoved", this.onRemove, this);
21272     },
21273
21274     // private
21275     onRefresh : function(){
21276         var ds = this.grid.dataSource, i, v = this.grid.view;
21277         var s = this.selections;
21278         s.each(function(r){
21279             if((i = ds.indexOfId(r.id)) != -1){
21280                 v.onRowSelect(i);
21281             }else{
21282                 s.remove(r);
21283             }
21284         });
21285     },
21286
21287     // private
21288     onRemove : function(v, index, r){
21289         this.selections.remove(r);
21290     },
21291
21292     // private
21293     onRowUpdated : function(v, index, r){
21294         if(this.isSelected(r)){
21295             v.onRowSelect(index);
21296         }
21297     },
21298
21299     /**
21300      * Select records.
21301      * @param {Array} records The records to select
21302      * @param {Boolean} keepExisting (optional) True to keep existing selections
21303      */
21304     selectRecords : function(records, keepExisting){
21305         if(!keepExisting){
21306             this.clearSelections();
21307         }
21308         var ds = this.grid.dataSource;
21309         for(var i = 0, len = records.length; i < len; i++){
21310             this.selectRow(ds.indexOf(records[i]), true);
21311         }
21312     },
21313
21314     /**
21315      * Gets the number of selected rows.
21316      * @return {Number}
21317      */
21318     getCount : function(){
21319         return this.selections.length;
21320     },
21321
21322     /**
21323      * Selects the first row in the grid.
21324      */
21325     selectFirstRow : function(){
21326         this.selectRow(0);
21327     },
21328
21329     /**
21330      * Select the last row.
21331      * @param {Boolean} keepExisting (optional) True to keep existing selections
21332      */
21333     selectLastRow : function(keepExisting){
21334         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21335     },
21336
21337     /**
21338      * Selects the row immediately following the last selected row.
21339      * @param {Boolean} keepExisting (optional) True to keep existing selections
21340      */
21341     selectNext : function(keepExisting){
21342         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21343             this.selectRow(this.last+1, keepExisting);
21344             this.grid.getView().focusRow(this.last);
21345         }
21346     },
21347
21348     /**
21349      * Selects the row that precedes the last selected row.
21350      * @param {Boolean} keepExisting (optional) True to keep existing selections
21351      */
21352     selectPrevious : function(keepExisting){
21353         if(this.last){
21354             this.selectRow(this.last-1, keepExisting);
21355             this.grid.getView().focusRow(this.last);
21356         }
21357     },
21358
21359     /**
21360      * Returns the selected records
21361      * @return {Array} Array of selected records
21362      */
21363     getSelections : function(){
21364         return [].concat(this.selections.items);
21365     },
21366
21367     /**
21368      * Returns the first selected record.
21369      * @return {Record}
21370      */
21371     getSelected : function(){
21372         return this.selections.itemAt(0);
21373     },
21374
21375
21376     /**
21377      * Clears all selections.
21378      */
21379     clearSelections : function(fast){
21380         if(this.locked) return;
21381         if(fast !== true){
21382             var ds = this.grid.dataSource;
21383             var s = this.selections;
21384             s.each(function(r){
21385                 this.deselectRow(ds.indexOfId(r.id));
21386             }, this);
21387             s.clear();
21388         }else{
21389             this.selections.clear();
21390         }
21391         this.last = false;
21392     },
21393
21394
21395     /**
21396      * Selects all rows.
21397      */
21398     selectAll : function(){
21399         if(this.locked) return;
21400         this.selections.clear();
21401         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21402             this.selectRow(i, true);
21403         }
21404     },
21405
21406     /**
21407      * Returns True if there is a selection.
21408      * @return {Boolean}
21409      */
21410     hasSelection : function(){
21411         return this.selections.length > 0;
21412     },
21413
21414     /**
21415      * Returns True if the specified row is selected.
21416      * @param {Number/Record} record The record or index of the record to check
21417      * @return {Boolean}
21418      */
21419     isSelected : function(index){
21420         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21421         return (r && this.selections.key(r.id) ? true : false);
21422     },
21423
21424     /**
21425      * Returns True if the specified record id is selected.
21426      * @param {String} id The id of record to check
21427      * @return {Boolean}
21428      */
21429     isIdSelected : function(id){
21430         return (this.selections.key(id) ? true : false);
21431     },
21432
21433     // private
21434     handleMouseDown : function(e, t){
21435         var view = this.grid.getView(), rowIndex;
21436         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21437             return;
21438         };
21439         if(e.shiftKey && this.last !== false){
21440             var last = this.last;
21441             this.selectRange(last, rowIndex, e.ctrlKey);
21442             this.last = last; // reset the last
21443             view.focusRow(rowIndex);
21444         }else{
21445             var isSelected = this.isSelected(rowIndex);
21446             if(e.button !== 0 && isSelected){
21447                 view.focusRow(rowIndex);
21448             }else if(e.ctrlKey && isSelected){
21449                 this.deselectRow(rowIndex);
21450             }else if(!isSelected){
21451                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21452                 view.focusRow(rowIndex);
21453             }
21454         }
21455         this.fireEvent("afterselectionchange", this);
21456     },
21457     // private
21458     handleDragableRowClick :  function(grid, rowIndex, e) 
21459     {
21460         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21461             this.selectRow(rowIndex, false);
21462             grid.view.focusRow(rowIndex);
21463              this.fireEvent("afterselectionchange", this);
21464         }
21465     },
21466     
21467     /**
21468      * Selects multiple rows.
21469      * @param {Array} rows Array of the indexes of the row to select
21470      * @param {Boolean} keepExisting (optional) True to keep existing selections
21471      */
21472     selectRows : function(rows, keepExisting){
21473         if(!keepExisting){
21474             this.clearSelections();
21475         }
21476         for(var i = 0, len = rows.length; i < len; i++){
21477             this.selectRow(rows[i], true);
21478         }
21479     },
21480
21481     /**
21482      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21483      * @param {Number} startRow The index of the first row in the range
21484      * @param {Number} endRow The index of the last row in the range
21485      * @param {Boolean} keepExisting (optional) True to retain existing selections
21486      */
21487     selectRange : function(startRow, endRow, keepExisting){
21488         if(this.locked) return;
21489         if(!keepExisting){
21490             this.clearSelections();
21491         }
21492         if(startRow <= endRow){
21493             for(var i = startRow; i <= endRow; i++){
21494                 this.selectRow(i, true);
21495             }
21496         }else{
21497             for(var i = startRow; i >= endRow; i--){
21498                 this.selectRow(i, true);
21499             }
21500         }
21501     },
21502
21503     /**
21504      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21505      * @param {Number} startRow The index of the first row in the range
21506      * @param {Number} endRow The index of the last row in the range
21507      */
21508     deselectRange : function(startRow, endRow, preventViewNotify){
21509         if(this.locked) return;
21510         for(var i = startRow; i <= endRow; i++){
21511             this.deselectRow(i, preventViewNotify);
21512         }
21513     },
21514
21515     /**
21516      * Selects a row.
21517      * @param {Number} row The index of the row to select
21518      * @param {Boolean} keepExisting (optional) True to keep existing selections
21519      */
21520     selectRow : function(index, keepExisting, preventViewNotify){
21521         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21522         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21523             if(!keepExisting || this.singleSelect){
21524                 this.clearSelections();
21525             }
21526             var r = this.grid.dataSource.getAt(index);
21527             this.selections.add(r);
21528             this.last = this.lastActive = index;
21529             if(!preventViewNotify){
21530                 this.grid.getView().onRowSelect(index);
21531             }
21532             this.fireEvent("rowselect", this, index, r);
21533             this.fireEvent("selectionchange", this);
21534         }
21535     },
21536
21537     /**
21538      * Deselects a row.
21539      * @param {Number} row The index of the row to deselect
21540      */
21541     deselectRow : function(index, preventViewNotify){
21542         if(this.locked) return;
21543         if(this.last == index){
21544             this.last = false;
21545         }
21546         if(this.lastActive == index){
21547             this.lastActive = false;
21548         }
21549         var r = this.grid.dataSource.getAt(index);
21550         this.selections.remove(r);
21551         if(!preventViewNotify){
21552             this.grid.getView().onRowDeselect(index);
21553         }
21554         this.fireEvent("rowdeselect", this, index);
21555         this.fireEvent("selectionchange", this);
21556     },
21557
21558     // private
21559     restoreLast : function(){
21560         if(this._last){
21561             this.last = this._last;
21562         }
21563     },
21564
21565     // private
21566     acceptsNav : function(row, col, cm){
21567         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21568     },
21569
21570     // private
21571     onEditorKey : function(field, e){
21572         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21573         if(k == e.TAB){
21574             e.stopEvent();
21575             ed.completeEdit();
21576             if(e.shiftKey){
21577                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21578             }else{
21579                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21580             }
21581         }else if(k == e.ENTER && !e.ctrlKey){
21582             e.stopEvent();
21583             ed.completeEdit();
21584             if(e.shiftKey){
21585                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21586             }else{
21587                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21588             }
21589         }else if(k == e.ESC){
21590             ed.cancelEdit();
21591         }
21592         if(newCell){
21593             g.startEditing(newCell[0], newCell[1]);
21594         }
21595     }
21596 });/*
21597  * Based on:
21598  * Ext JS Library 1.1.1
21599  * Copyright(c) 2006-2007, Ext JS, LLC.
21600  *
21601  * Originally Released Under LGPL - original licence link has changed is not relivant.
21602  *
21603  * Fork - LGPL
21604  * <script type="text/javascript">
21605  */
21606  
21607 /**
21608  * @class Roo.bootstrap.PagingToolbar
21609  * @extends Roo.Row
21610  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21611  * @constructor
21612  * Create a new PagingToolbar
21613  * @param {Object} config The config object
21614  */
21615 Roo.bootstrap.PagingToolbar = function(config)
21616 {
21617     // old args format still supported... - xtype is prefered..
21618         // created from xtype...
21619     var ds = config.dataSource;
21620     this.toolbarItems = [];
21621     if (config.items) {
21622         this.toolbarItems = config.items;
21623 //        config.items = [];
21624     }
21625     
21626     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21627     this.ds = ds;
21628     this.cursor = 0;
21629     if (ds) { 
21630         this.bind(ds);
21631     }
21632     
21633     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21634     
21635 };
21636
21637 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21638     /**
21639      * @cfg {Roo.data.Store} dataSource
21640      * The underlying data store providing the paged data
21641      */
21642     /**
21643      * @cfg {String/HTMLElement/Element} container
21644      * container The id or element that will contain the toolbar
21645      */
21646     /**
21647      * @cfg {Boolean} displayInfo
21648      * True to display the displayMsg (defaults to false)
21649      */
21650     /**
21651      * @cfg {Number} pageSize
21652      * The number of records to display per page (defaults to 20)
21653      */
21654     pageSize: 20,
21655     /**
21656      * @cfg {String} displayMsg
21657      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21658      */
21659     displayMsg : 'Displaying {0} - {1} of {2}',
21660     /**
21661      * @cfg {String} emptyMsg
21662      * The message to display when no records are found (defaults to "No data to display")
21663      */
21664     emptyMsg : 'No data to display',
21665     /**
21666      * Customizable piece of the default paging text (defaults to "Page")
21667      * @type String
21668      */
21669     beforePageText : "Page",
21670     /**
21671      * Customizable piece of the default paging text (defaults to "of %0")
21672      * @type String
21673      */
21674     afterPageText : "of {0}",
21675     /**
21676      * Customizable piece of the default paging text (defaults to "First Page")
21677      * @type String
21678      */
21679     firstText : "First Page",
21680     /**
21681      * Customizable piece of the default paging text (defaults to "Previous Page")
21682      * @type String
21683      */
21684     prevText : "Previous Page",
21685     /**
21686      * Customizable piece of the default paging text (defaults to "Next Page")
21687      * @type String
21688      */
21689     nextText : "Next Page",
21690     /**
21691      * Customizable piece of the default paging text (defaults to "Last Page")
21692      * @type String
21693      */
21694     lastText : "Last Page",
21695     /**
21696      * Customizable piece of the default paging text (defaults to "Refresh")
21697      * @type String
21698      */
21699     refreshText : "Refresh",
21700
21701     buttons : false,
21702     // private
21703     onRender : function(ct, position) 
21704     {
21705         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21706         this.navgroup.parentId = this.id;
21707         this.navgroup.onRender(this.el, null);
21708         // add the buttons to the navgroup
21709         
21710         if(this.displayInfo){
21711             Roo.log(this.el.select('ul.navbar-nav',true).first());
21712             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21713             this.displayEl = this.el.select('.x-paging-info', true).first();
21714 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21715 //            this.displayEl = navel.el.select('span',true).first();
21716         }
21717         
21718         var _this = this;
21719         
21720         if(this.buttons){
21721             Roo.each(_this.buttons, function(e){
21722                Roo.factory(e).onRender(_this.el, null);
21723             });
21724         }
21725             
21726         Roo.each(_this.toolbarItems, function(e) {
21727             _this.navgroup.addItem(e);
21728         });
21729         
21730         
21731         this.first = this.navgroup.addItem({
21732             tooltip: this.firstText,
21733             cls: "prev",
21734             icon : 'fa fa-backward',
21735             disabled: true,
21736             preventDefault: true,
21737             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21738         });
21739         
21740         this.prev =  this.navgroup.addItem({
21741             tooltip: this.prevText,
21742             cls: "prev",
21743             icon : 'fa fa-step-backward',
21744             disabled: true,
21745             preventDefault: true,
21746             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21747         });
21748     //this.addSeparator();
21749         
21750         
21751         var field = this.navgroup.addItem( {
21752             tagtype : 'span',
21753             cls : 'x-paging-position',
21754             
21755             html : this.beforePageText  +
21756                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21757                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21758          } ); //?? escaped?
21759         
21760         this.field = field.el.select('input', true).first();
21761         this.field.on("keydown", this.onPagingKeydown, this);
21762         this.field.on("focus", function(){this.dom.select();});
21763     
21764     
21765         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21766         //this.field.setHeight(18);
21767         //this.addSeparator();
21768         this.next = this.navgroup.addItem({
21769             tooltip: this.nextText,
21770             cls: "next",
21771             html : ' <i class="fa fa-step-forward">',
21772             disabled: true,
21773             preventDefault: true,
21774             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21775         });
21776         this.last = this.navgroup.addItem({
21777             tooltip: this.lastText,
21778             icon : 'fa fa-forward',
21779             cls: "next",
21780             disabled: true,
21781             preventDefault: true,
21782             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21783         });
21784     //this.addSeparator();
21785         this.loading = this.navgroup.addItem({
21786             tooltip: this.refreshText,
21787             icon: 'fa fa-refresh',
21788             preventDefault: true,
21789             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21790         });
21791
21792     },
21793
21794     // private
21795     updateInfo : function(){
21796         if(this.displayEl){
21797             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21798             var msg = count == 0 ?
21799                 this.emptyMsg :
21800                 String.format(
21801                     this.displayMsg,
21802                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21803                 );
21804             this.displayEl.update(msg);
21805         }
21806     },
21807
21808     // private
21809     onLoad : function(ds, r, o){
21810        this.cursor = o.params ? o.params.start : 0;
21811        var d = this.getPageData(),
21812             ap = d.activePage,
21813             ps = d.pages;
21814         
21815        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21816        this.field.dom.value = ap;
21817        this.first.setDisabled(ap == 1);
21818        this.prev.setDisabled(ap == 1);
21819        this.next.setDisabled(ap == ps);
21820        this.last.setDisabled(ap == ps);
21821        this.loading.enable();
21822        this.updateInfo();
21823     },
21824
21825     // private
21826     getPageData : function(){
21827         var total = this.ds.getTotalCount();
21828         return {
21829             total : total,
21830             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21831             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21832         };
21833     },
21834
21835     // private
21836     onLoadError : function(){
21837         this.loading.enable();
21838     },
21839
21840     // private
21841     onPagingKeydown : function(e){
21842         var k = e.getKey();
21843         var d = this.getPageData();
21844         if(k == e.RETURN){
21845             var v = this.field.dom.value, pageNum;
21846             if(!v || isNaN(pageNum = parseInt(v, 10))){
21847                 this.field.dom.value = d.activePage;
21848                 return;
21849             }
21850             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21851             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21852             e.stopEvent();
21853         }
21854         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))
21855         {
21856           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21857           this.field.dom.value = pageNum;
21858           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21859           e.stopEvent();
21860         }
21861         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21862         {
21863           var v = this.field.dom.value, pageNum; 
21864           var increment = (e.shiftKey) ? 10 : 1;
21865           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21866             increment *= -1;
21867           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21868             this.field.dom.value = d.activePage;
21869             return;
21870           }
21871           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21872           {
21873             this.field.dom.value = parseInt(v, 10) + increment;
21874             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21875             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21876           }
21877           e.stopEvent();
21878         }
21879     },
21880
21881     // private
21882     beforeLoad : function(){
21883         if(this.loading){
21884             this.loading.disable();
21885         }
21886     },
21887
21888     // private
21889     onClick : function(which){
21890         
21891         var ds = this.ds;
21892         if (!ds) {
21893             return;
21894         }
21895         
21896         switch(which){
21897             case "first":
21898                 ds.load({params:{start: 0, limit: this.pageSize}});
21899             break;
21900             case "prev":
21901                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21902             break;
21903             case "next":
21904                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21905             break;
21906             case "last":
21907                 var total = ds.getTotalCount();
21908                 var extra = total % this.pageSize;
21909                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21910                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21911             break;
21912             case "refresh":
21913                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21914             break;
21915         }
21916     },
21917
21918     /**
21919      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21920      * @param {Roo.data.Store} store The data store to unbind
21921      */
21922     unbind : function(ds){
21923         ds.un("beforeload", this.beforeLoad, this);
21924         ds.un("load", this.onLoad, this);
21925         ds.un("loadexception", this.onLoadError, this);
21926         ds.un("remove", this.updateInfo, this);
21927         ds.un("add", this.updateInfo, this);
21928         this.ds = undefined;
21929     },
21930
21931     /**
21932      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21933      * @param {Roo.data.Store} store The data store to bind
21934      */
21935     bind : function(ds){
21936         ds.on("beforeload", this.beforeLoad, this);
21937         ds.on("load", this.onLoad, this);
21938         ds.on("loadexception", this.onLoadError, this);
21939         ds.on("remove", this.updateInfo, this);
21940         ds.on("add", this.updateInfo, this);
21941         this.ds = ds;
21942     }
21943 });/*
21944  * - LGPL
21945  *
21946  * element
21947  * 
21948  */
21949
21950 /**
21951  * @class Roo.bootstrap.MessageBar
21952  * @extends Roo.bootstrap.Component
21953  * Bootstrap MessageBar class
21954  * @cfg {String} html contents of the MessageBar
21955  * @cfg {String} weight (info | success | warning | danger) default info
21956  * @cfg {String} beforeClass insert the bar before the given class
21957  * @cfg {Boolean} closable (true | false) default false
21958  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21959  * 
21960  * @constructor
21961  * Create a new Element
21962  * @param {Object} config The config object
21963  */
21964
21965 Roo.bootstrap.MessageBar = function(config){
21966     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21967 };
21968
21969 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21970     
21971     html: '',
21972     weight: 'info',
21973     closable: false,
21974     fixed: false,
21975     beforeClass: 'bootstrap-sticky-wrap',
21976     
21977     getAutoCreate : function(){
21978         
21979         var cfg = {
21980             tag: 'div',
21981             cls: 'alert alert-dismissable alert-' + this.weight,
21982             cn: [
21983                 {
21984                     tag: 'span',
21985                     cls: 'message',
21986                     html: this.html || ''
21987                 }
21988             ]
21989         }
21990         
21991         if(this.fixed){
21992             cfg.cls += ' alert-messages-fixed';
21993         }
21994         
21995         if(this.closable){
21996             cfg.cn.push({
21997                 tag: 'button',
21998                 cls: 'close',
21999                 html: 'x'
22000             });
22001         }
22002         
22003         return cfg;
22004     },
22005     
22006     onRender : function(ct, position)
22007     {
22008         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22009         
22010         if(!this.el){
22011             var cfg = Roo.apply({},  this.getAutoCreate());
22012             cfg.id = Roo.id();
22013             
22014             if (this.cls) {
22015                 cfg.cls += ' ' + this.cls;
22016             }
22017             if (this.style) {
22018                 cfg.style = this.style;
22019             }
22020             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22021             
22022             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22023         }
22024         
22025         this.el.select('>button.close').on('click', this.hide, this);
22026         
22027     },
22028     
22029     show : function()
22030     {
22031         if (!this.rendered) {
22032             this.render();
22033         }
22034         
22035         this.el.show();
22036         
22037         this.fireEvent('show', this);
22038         
22039     },
22040     
22041     hide : function()
22042     {
22043         if (!this.rendered) {
22044             this.render();
22045         }
22046         
22047         this.el.hide();
22048         
22049         this.fireEvent('hide', this);
22050     },
22051     
22052     update : function()
22053     {
22054 //        var e = this.el.dom.firstChild;
22055 //        
22056 //        if(this.closable){
22057 //            e = e.nextSibling;
22058 //        }
22059 //        
22060 //        e.data = this.html || '';
22061
22062         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22063     }
22064    
22065 });
22066
22067  
22068
22069      /*
22070  * - LGPL
22071  *
22072  * Graph
22073  * 
22074  */
22075
22076
22077 /**
22078  * @class Roo.bootstrap.Graph
22079  * @extends Roo.bootstrap.Component
22080  * Bootstrap Graph class
22081 > Prameters
22082  -sm {number} sm 4
22083  -md {number} md 5
22084  @cfg {String} graphtype  bar | vbar | pie
22085  @cfg {number} g_x coodinator | centre x (pie)
22086  @cfg {number} g_y coodinator | centre y (pie)
22087  @cfg {number} g_r radius (pie)
22088  @cfg {number} g_height height of the chart (respected by all elements in the set)
22089  @cfg {number} g_width width of the chart (respected by all elements in the set)
22090  @cfg {Object} title The title of the chart
22091     
22092  -{Array}  values
22093  -opts (object) options for the chart 
22094      o {
22095      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22096      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22097      o vgutter (number)
22098      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.
22099      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22100      o to
22101      o stretch (boolean)
22102      o }
22103  -opts (object) options for the pie
22104      o{
22105      o cut
22106      o startAngle (number)
22107      o endAngle (number)
22108      } 
22109  *
22110  * @constructor
22111  * Create a new Input
22112  * @param {Object} config The config object
22113  */
22114
22115 Roo.bootstrap.Graph = function(config){
22116     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22117     
22118     this.addEvents({
22119         // img events
22120         /**
22121          * @event click
22122          * The img click event for the img.
22123          * @param {Roo.EventObject} e
22124          */
22125         "click" : true
22126     });
22127 };
22128
22129 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22130     
22131     sm: 4,
22132     md: 5,
22133     graphtype: 'bar',
22134     g_height: 250,
22135     g_width: 400,
22136     g_x: 50,
22137     g_y: 50,
22138     g_r: 30,
22139     opts:{
22140         //g_colors: this.colors,
22141         g_type: 'soft',
22142         g_gutter: '20%'
22143
22144     },
22145     title : false,
22146
22147     getAutoCreate : function(){
22148         
22149         var cfg = {
22150             tag: 'div',
22151             html : null
22152         }
22153         
22154         
22155         return  cfg;
22156     },
22157
22158     onRender : function(ct,position){
22159         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22160         this.raphael = Raphael(this.el.dom);
22161         
22162                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22163                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22164                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22165                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22166                 /*
22167                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22168                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22169                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22170                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22171                 
22172                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22173                 r.barchart(330, 10, 300, 220, data1);
22174                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22175                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22176                 */
22177                 
22178                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22179                 // r.barchart(30, 30, 560, 250,  xdata, {
22180                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22181                 //     axis : "0 0 1 1",
22182                 //     axisxlabels :  xdata
22183                 //     //yvalues : cols,
22184                    
22185                 // });
22186 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22187 //        
22188 //        this.load(null,xdata,{
22189 //                axis : "0 0 1 1",
22190 //                axisxlabels :  xdata
22191 //                });
22192
22193     },
22194
22195     load : function(graphtype,xdata,opts){
22196         this.raphael.clear();
22197         if(!graphtype) {
22198             graphtype = this.graphtype;
22199         }
22200         if(!opts){
22201             opts = this.opts;
22202         }
22203         var r = this.raphael,
22204             fin = function () {
22205                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22206             },
22207             fout = function () {
22208                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22209             },
22210             pfin = function() {
22211                 this.sector.stop();
22212                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22213
22214                 if (this.label) {
22215                     this.label[0].stop();
22216                     this.label[0].attr({ r: 7.5 });
22217                     this.label[1].attr({ "font-weight": 800 });
22218                 }
22219             },
22220             pfout = function() {
22221                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22222
22223                 if (this.label) {
22224                     this.label[0].animate({ r: 5 }, 500, "bounce");
22225                     this.label[1].attr({ "font-weight": 400 });
22226                 }
22227             };
22228
22229         switch(graphtype){
22230             case 'bar':
22231                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22232                 break;
22233             case 'hbar':
22234                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22235                 break;
22236             case 'pie':
22237 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22238 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22239 //            
22240                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22241                 
22242                 break;
22243
22244         }
22245         
22246         if(this.title){
22247             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22248         }
22249         
22250     },
22251     
22252     setTitle: function(o)
22253     {
22254         this.title = o;
22255     },
22256     
22257     initEvents: function() {
22258         
22259         if(!this.href){
22260             this.el.on('click', this.onClick, this);
22261         }
22262     },
22263     
22264     onClick : function(e)
22265     {
22266         Roo.log('img onclick');
22267         this.fireEvent('click', this, e);
22268     }
22269    
22270 });
22271
22272  
22273 /*
22274  * - LGPL
22275  *
22276  * numberBox
22277  * 
22278  */
22279 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22280
22281 /**
22282  * @class Roo.bootstrap.dash.NumberBox
22283  * @extends Roo.bootstrap.Component
22284  * Bootstrap NumberBox class
22285  * @cfg {String} headline Box headline
22286  * @cfg {String} content Box content
22287  * @cfg {String} icon Box icon
22288  * @cfg {String} footer Footer text
22289  * @cfg {String} fhref Footer href
22290  * 
22291  * @constructor
22292  * Create a new NumberBox
22293  * @param {Object} config The config object
22294  */
22295
22296
22297 Roo.bootstrap.dash.NumberBox = function(config){
22298     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22299     
22300 };
22301
22302 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22303     
22304     headline : '',
22305     content : '',
22306     icon : '',
22307     footer : '',
22308     fhref : '',
22309     ficon : '',
22310     
22311     getAutoCreate : function(){
22312         
22313         var cfg = {
22314             tag : 'div',
22315             cls : 'small-box ',
22316             cn : [
22317                 {
22318                     tag : 'div',
22319                     cls : 'inner',
22320                     cn :[
22321                         {
22322                             tag : 'h3',
22323                             cls : 'roo-headline',
22324                             html : this.headline
22325                         },
22326                         {
22327                             tag : 'p',
22328                             cls : 'roo-content',
22329                             html : this.content
22330                         }
22331                     ]
22332                 }
22333             ]
22334         }
22335         
22336         if(this.icon){
22337             cfg.cn.push({
22338                 tag : 'div',
22339                 cls : 'icon',
22340                 cn :[
22341                     {
22342                         tag : 'i',
22343                         cls : 'ion ' + this.icon
22344                     }
22345                 ]
22346             });
22347         }
22348         
22349         if(this.footer){
22350             var footer = {
22351                 tag : 'a',
22352                 cls : 'small-box-footer',
22353                 href : this.fhref || '#',
22354                 html : this.footer
22355             };
22356             
22357             cfg.cn.push(footer);
22358             
22359         }
22360         
22361         return  cfg;
22362     },
22363
22364     onRender : function(ct,position){
22365         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22366
22367
22368        
22369                 
22370     },
22371
22372     setHeadline: function (value)
22373     {
22374         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22375     },
22376     
22377     setFooter: function (value, href)
22378     {
22379         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22380         
22381         if(href){
22382             this.el.select('a.small-box-footer',true).first().attr('href', href);
22383         }
22384         
22385     },
22386
22387     setContent: function (value)
22388     {
22389         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22390     },
22391
22392     initEvents: function() 
22393     {   
22394         
22395     }
22396     
22397 });
22398
22399  
22400 /*
22401  * - LGPL
22402  *
22403  * TabBox
22404  * 
22405  */
22406 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22407
22408 /**
22409  * @class Roo.bootstrap.dash.TabBox
22410  * @extends Roo.bootstrap.Component
22411  * Bootstrap TabBox class
22412  * @cfg {String} title Title of the TabBox
22413  * @cfg {String} icon Icon of the TabBox
22414  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22415  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22416  * 
22417  * @constructor
22418  * Create a new TabBox
22419  * @param {Object} config The config object
22420  */
22421
22422
22423 Roo.bootstrap.dash.TabBox = function(config){
22424     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22425     this.addEvents({
22426         // raw events
22427         /**
22428          * @event addpane
22429          * When a pane is added
22430          * @param {Roo.bootstrap.dash.TabPane} pane
22431          */
22432         "addpane" : true,
22433         /**
22434          * @event activatepane
22435          * When a pane is activated
22436          * @param {Roo.bootstrap.dash.TabPane} pane
22437          */
22438         "activatepane" : true
22439         
22440          
22441     });
22442     
22443     this.panes = [];
22444 };
22445
22446 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22447
22448     title : '',
22449     icon : false,
22450     showtabs : true,
22451     tabScrollable : false,
22452     
22453     getChildContainer : function()
22454     {
22455         return this.el.select('.tab-content', true).first();
22456     },
22457     
22458     getAutoCreate : function(){
22459         
22460         var header = {
22461             tag: 'li',
22462             cls: 'pull-left header',
22463             html: this.title,
22464             cn : []
22465         };
22466         
22467         if(this.icon){
22468             header.cn.push({
22469                 tag: 'i',
22470                 cls: 'fa ' + this.icon
22471             });
22472         }
22473         
22474         var h = {
22475             tag: 'ul',
22476             cls: 'nav nav-tabs pull-right',
22477             cn: [
22478                 header
22479             ]
22480         };
22481         
22482         if(this.tabScrollable){
22483             h = {
22484                 tag: 'div',
22485                 cls: 'tab-header',
22486                 cn: [
22487                     {
22488                         tag: 'ul',
22489                         cls: 'nav nav-tabs pull-right',
22490                         cn: [
22491                             header
22492                         ]
22493                     }
22494                 ]
22495             }
22496         }
22497         
22498         var cfg = {
22499             tag: 'div',
22500             cls: 'nav-tabs-custom',
22501             cn: [
22502                 h,
22503                 {
22504                     tag: 'div',
22505                     cls: 'tab-content no-padding',
22506                     cn: []
22507                 }
22508             ]
22509         }
22510
22511         return  cfg;
22512     },
22513     initEvents : function()
22514     {
22515         //Roo.log('add add pane handler');
22516         this.on('addpane', this.onAddPane, this);
22517     },
22518      /**
22519      * Updates the box title
22520      * @param {String} html to set the title to.
22521      */
22522     setTitle : function(value)
22523     {
22524         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22525     },
22526     onAddPane : function(pane)
22527     {
22528         this.panes.push(pane);
22529         //Roo.log('addpane');
22530         //Roo.log(pane);
22531         // tabs are rendere left to right..
22532         if(!this.showtabs){
22533             return;
22534         }
22535         
22536         var ctr = this.el.select('.nav-tabs', true).first();
22537          
22538          
22539         var existing = ctr.select('.nav-tab',true);
22540         var qty = existing.getCount();;
22541         
22542         
22543         var tab = ctr.createChild({
22544             tag : 'li',
22545             cls : 'nav-tab' + (qty ? '' : ' active'),
22546             cn : [
22547                 {
22548                     tag : 'a',
22549                     href:'#',
22550                     html : pane.title
22551                 }
22552             ]
22553         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22554         pane.tab = tab;
22555         
22556         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22557         if (!qty) {
22558             pane.el.addClass('active');
22559         }
22560         
22561                 
22562     },
22563     onTabClick : function(ev,un,ob,pane)
22564     {
22565         //Roo.log('tab - prev default');
22566         ev.preventDefault();
22567         
22568         
22569         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22570         pane.tab.addClass('active');
22571         //Roo.log(pane.title);
22572         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22573         // technically we should have a deactivate event.. but maybe add later.
22574         // and it should not de-activate the selected tab...
22575         this.fireEvent('activatepane', pane);
22576         pane.el.addClass('active');
22577         pane.fireEvent('activate');
22578         
22579         
22580     },
22581     
22582     getActivePane : function()
22583     {
22584         var r = false;
22585         Roo.each(this.panes, function(p) {
22586             if(p.el.hasClass('active')){
22587                 r = p;
22588                 return false;
22589             }
22590             
22591             return;
22592         });
22593         
22594         return r;
22595     }
22596     
22597     
22598 });
22599
22600  
22601 /*
22602  * - LGPL
22603  *
22604  * Tab pane
22605  * 
22606  */
22607 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22608 /**
22609  * @class Roo.bootstrap.TabPane
22610  * @extends Roo.bootstrap.Component
22611  * Bootstrap TabPane class
22612  * @cfg {Boolean} active (false | true) Default false
22613  * @cfg {String} title title of panel
22614
22615  * 
22616  * @constructor
22617  * Create a new TabPane
22618  * @param {Object} config The config object
22619  */
22620
22621 Roo.bootstrap.dash.TabPane = function(config){
22622     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22623     
22624     this.addEvents({
22625         // raw events
22626         /**
22627          * @event activate
22628          * When a pane is activated
22629          * @param {Roo.bootstrap.dash.TabPane} pane
22630          */
22631         "activate" : true
22632          
22633     });
22634 };
22635
22636 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22637     
22638     active : false,
22639     title : '',
22640     
22641     // the tabBox that this is attached to.
22642     tab : false,
22643      
22644     getAutoCreate : function() 
22645     {
22646         var cfg = {
22647             tag: 'div',
22648             cls: 'tab-pane'
22649         }
22650         
22651         if(this.active){
22652             cfg.cls += ' active';
22653         }
22654         
22655         return cfg;
22656     },
22657     initEvents  : function()
22658     {
22659         //Roo.log('trigger add pane handler');
22660         this.parent().fireEvent('addpane', this)
22661     },
22662     
22663      /**
22664      * Updates the tab title 
22665      * @param {String} html to set the title to.
22666      */
22667     setTitle: function(str)
22668     {
22669         if (!this.tab) {
22670             return;
22671         }
22672         this.title = str;
22673         this.tab.select('a', true).first().dom.innerHTML = str;
22674         
22675     }
22676     
22677     
22678     
22679 });
22680
22681  
22682
22683
22684  /*
22685  * - LGPL
22686  *
22687  * menu
22688  * 
22689  */
22690 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22691
22692 /**
22693  * @class Roo.bootstrap.menu.Menu
22694  * @extends Roo.bootstrap.Component
22695  * Bootstrap Menu class - container for Menu
22696  * @cfg {String} html Text of the menu
22697  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22698  * @cfg {String} icon Font awesome icon
22699  * @cfg {String} pos Menu align to (top | bottom) default bottom
22700  * 
22701  * 
22702  * @constructor
22703  * Create a new Menu
22704  * @param {Object} config The config object
22705  */
22706
22707
22708 Roo.bootstrap.menu.Menu = function(config){
22709     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22710     
22711     this.addEvents({
22712         /**
22713          * @event beforeshow
22714          * Fires before this menu is displayed
22715          * @param {Roo.bootstrap.menu.Menu} this
22716          */
22717         beforeshow : true,
22718         /**
22719          * @event beforehide
22720          * Fires before this menu is hidden
22721          * @param {Roo.bootstrap.menu.Menu} this
22722          */
22723         beforehide : true,
22724         /**
22725          * @event show
22726          * Fires after this menu is displayed
22727          * @param {Roo.bootstrap.menu.Menu} this
22728          */
22729         show : true,
22730         /**
22731          * @event hide
22732          * Fires after this menu is hidden
22733          * @param {Roo.bootstrap.menu.Menu} this
22734          */
22735         hide : true,
22736         /**
22737          * @event click
22738          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22739          * @param {Roo.bootstrap.menu.Menu} this
22740          * @param {Roo.EventObject} e
22741          */
22742         click : true
22743     });
22744     
22745 };
22746
22747 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22748     
22749     submenu : false,
22750     html : '',
22751     weight : 'default',
22752     icon : false,
22753     pos : 'bottom',
22754     
22755     
22756     getChildContainer : function() {
22757         if(this.isSubMenu){
22758             return this.el;
22759         }
22760         
22761         return this.el.select('ul.dropdown-menu', true).first();  
22762     },
22763     
22764     getAutoCreate : function()
22765     {
22766         var text = [
22767             {
22768                 tag : 'span',
22769                 cls : 'roo-menu-text',
22770                 html : this.html
22771             }
22772         ];
22773         
22774         if(this.icon){
22775             text.unshift({
22776                 tag : 'i',
22777                 cls : 'fa ' + this.icon
22778             })
22779         }
22780         
22781         
22782         var cfg = {
22783             tag : 'div',
22784             cls : 'btn-group',
22785             cn : [
22786                 {
22787                     tag : 'button',
22788                     cls : 'dropdown-button btn btn-' + this.weight,
22789                     cn : text
22790                 },
22791                 {
22792                     tag : 'button',
22793                     cls : 'dropdown-toggle btn btn-' + this.weight,
22794                     cn : [
22795                         {
22796                             tag : 'span',
22797                             cls : 'caret'
22798                         }
22799                     ]
22800                 },
22801                 {
22802                     tag : 'ul',
22803                     cls : 'dropdown-menu'
22804                 }
22805             ]
22806             
22807         };
22808         
22809         if(this.pos == 'top'){
22810             cfg.cls += ' dropup';
22811         }
22812         
22813         if(this.isSubMenu){
22814             cfg = {
22815                 tag : 'ul',
22816                 cls : 'dropdown-menu'
22817             }
22818         }
22819         
22820         return cfg;
22821     },
22822     
22823     onRender : function(ct, position)
22824     {
22825         this.isSubMenu = ct.hasClass('dropdown-submenu');
22826         
22827         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22828     },
22829     
22830     initEvents : function() 
22831     {
22832         if(this.isSubMenu){
22833             return;
22834         }
22835         
22836         this.hidden = true;
22837         
22838         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22839         this.triggerEl.on('click', this.onTriggerPress, this);
22840         
22841         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22842         this.buttonEl.on('click', this.onClick, this);
22843         
22844     },
22845     
22846     list : function()
22847     {
22848         if(this.isSubMenu){
22849             return this.el;
22850         }
22851         
22852         return this.el.select('ul.dropdown-menu', true).first();
22853     },
22854     
22855     onClick : function(e)
22856     {
22857         this.fireEvent("click", this, e);
22858     },
22859     
22860     onTriggerPress  : function(e)
22861     {   
22862         if (this.isVisible()) {
22863             this.hide();
22864         } else {
22865             this.show();
22866         }
22867     },
22868     
22869     isVisible : function(){
22870         return !this.hidden;
22871     },
22872     
22873     show : function()
22874     {
22875         this.fireEvent("beforeshow", this);
22876         
22877         this.hidden = false;
22878         this.el.addClass('open');
22879         
22880         Roo.get(document).on("mouseup", this.onMouseUp, this);
22881         
22882         this.fireEvent("show", this);
22883         
22884         
22885     },
22886     
22887     hide : function()
22888     {
22889         this.fireEvent("beforehide", this);
22890         
22891         this.hidden = true;
22892         this.el.removeClass('open');
22893         
22894         Roo.get(document).un("mouseup", this.onMouseUp);
22895         
22896         this.fireEvent("hide", this);
22897     },
22898     
22899     onMouseUp : function()
22900     {
22901         this.hide();
22902     }
22903     
22904 });
22905
22906  
22907  /*
22908  * - LGPL
22909  *
22910  * menu item
22911  * 
22912  */
22913 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22914
22915 /**
22916  * @class Roo.bootstrap.menu.Item
22917  * @extends Roo.bootstrap.Component
22918  * Bootstrap MenuItem class
22919  * @cfg {Boolean} submenu (true | false) default false
22920  * @cfg {String} html text of the item
22921  * @cfg {String} href the link
22922  * @cfg {Boolean} disable (true | false) default false
22923  * @cfg {Boolean} preventDefault (true | false) default true
22924  * @cfg {String} icon Font awesome icon
22925  * @cfg {String} pos Submenu align to (left | right) default right 
22926  * 
22927  * 
22928  * @constructor
22929  * Create a new Item
22930  * @param {Object} config The config object
22931  */
22932
22933
22934 Roo.bootstrap.menu.Item = function(config){
22935     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22936     this.addEvents({
22937         /**
22938          * @event mouseover
22939          * Fires when the mouse is hovering over this menu
22940          * @param {Roo.bootstrap.menu.Item} this
22941          * @param {Roo.EventObject} e
22942          */
22943         mouseover : true,
22944         /**
22945          * @event mouseout
22946          * Fires when the mouse exits this menu
22947          * @param {Roo.bootstrap.menu.Item} this
22948          * @param {Roo.EventObject} e
22949          */
22950         mouseout : true,
22951         // raw events
22952         /**
22953          * @event click
22954          * The raw click event for the entire grid.
22955          * @param {Roo.EventObject} e
22956          */
22957         click : true
22958     });
22959 };
22960
22961 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22962     
22963     submenu : false,
22964     href : '',
22965     html : '',
22966     preventDefault: true,
22967     disable : false,
22968     icon : false,
22969     pos : 'right',
22970     
22971     getAutoCreate : function()
22972     {
22973         var text = [
22974             {
22975                 tag : 'span',
22976                 cls : 'roo-menu-item-text',
22977                 html : this.html
22978             }
22979         ];
22980         
22981         if(this.icon){
22982             text.unshift({
22983                 tag : 'i',
22984                 cls : 'fa ' + this.icon
22985             })
22986         }
22987         
22988         var cfg = {
22989             tag : 'li',
22990             cn : [
22991                 {
22992                     tag : 'a',
22993                     href : this.href || '#',
22994                     cn : text
22995                 }
22996             ]
22997         };
22998         
22999         if(this.disable){
23000             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23001         }
23002         
23003         if(this.submenu){
23004             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23005             
23006             if(this.pos == 'left'){
23007                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23008             }
23009         }
23010         
23011         return cfg;
23012     },
23013     
23014     initEvents : function() 
23015     {
23016         this.el.on('mouseover', this.onMouseOver, this);
23017         this.el.on('mouseout', this.onMouseOut, this);
23018         
23019         this.el.select('a', true).first().on('click', this.onClick, this);
23020         
23021     },
23022     
23023     onClick : function(e)
23024     {
23025         if(this.preventDefault){
23026             e.preventDefault();
23027         }
23028         
23029         this.fireEvent("click", this, e);
23030     },
23031     
23032     onMouseOver : function(e)
23033     {
23034         if(this.submenu && this.pos == 'left'){
23035             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23036         }
23037         
23038         this.fireEvent("mouseover", this, e);
23039     },
23040     
23041     onMouseOut : function(e)
23042     {
23043         this.fireEvent("mouseout", this, e);
23044     }
23045 });
23046
23047  
23048
23049  /*
23050  * - LGPL
23051  *
23052  * menu separator
23053  * 
23054  */
23055 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23056
23057 /**
23058  * @class Roo.bootstrap.menu.Separator
23059  * @extends Roo.bootstrap.Component
23060  * Bootstrap Separator class
23061  * 
23062  * @constructor
23063  * Create a new Separator
23064  * @param {Object} config The config object
23065  */
23066
23067
23068 Roo.bootstrap.menu.Separator = function(config){
23069     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23070 };
23071
23072 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23073     
23074     getAutoCreate : function(){
23075         var cfg = {
23076             tag : 'li',
23077             cls: 'divider'
23078         };
23079         
23080         return cfg;
23081     }
23082    
23083 });
23084
23085  
23086
23087  /*
23088  * - LGPL
23089  *
23090  * Tooltip
23091  * 
23092  */
23093
23094 /**
23095  * @class Roo.bootstrap.Tooltip
23096  * Bootstrap Tooltip class
23097  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23098  * to determine which dom element triggers the tooltip.
23099  * 
23100  * It needs to add support for additional attributes like tooltip-position
23101  * 
23102  * @constructor
23103  * Create a new Toolti
23104  * @param {Object} config The config object
23105  */
23106
23107 Roo.bootstrap.Tooltip = function(config){
23108     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23109 };
23110
23111 Roo.apply(Roo.bootstrap.Tooltip, {
23112     /**
23113      * @function init initialize tooltip monitoring.
23114      * @static
23115      */
23116     currentEl : false,
23117     currentTip : false,
23118     currentRegion : false,
23119     
23120     //  init : delay?
23121     
23122     init : function()
23123     {
23124         Roo.get(document).on('mouseover', this.enter ,this);
23125         Roo.get(document).on('mouseout', this.leave, this);
23126          
23127         
23128         this.currentTip = new Roo.bootstrap.Tooltip();
23129     },
23130     
23131     enter : function(ev)
23132     {
23133         var dom = ev.getTarget();
23134         
23135         //Roo.log(['enter',dom]);
23136         var el = Roo.fly(dom);
23137         if (this.currentEl) {
23138             //Roo.log(dom);
23139             //Roo.log(this.currentEl);
23140             //Roo.log(this.currentEl.contains(dom));
23141             if (this.currentEl == el) {
23142                 return;
23143             }
23144             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23145                 return;
23146             }
23147
23148         }
23149         
23150         
23151         
23152         if (this.currentTip.el) {
23153             this.currentTip.el.hide(); // force hiding...
23154         }    
23155         //Roo.log(ev);
23156         var bindEl = el;
23157         
23158         // you can not look for children, as if el is the body.. then everythign is the child..
23159         if (!el.attr('tooltip')) { //
23160             if (!el.select("[tooltip]").elements.length) {
23161                 return;
23162             }
23163             // is the mouse over this child...?
23164             bindEl = el.select("[tooltip]").first();
23165             var xy = ev.getXY();
23166             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23167                 //Roo.log("not in region.");
23168                 return;
23169             }
23170             //Roo.log("child element over..");
23171             
23172         }
23173         this.currentEl = bindEl;
23174         this.currentTip.bind(bindEl);
23175         this.currentRegion = Roo.lib.Region.getRegion(dom);
23176         this.currentTip.enter();
23177         
23178     },
23179     leave : function(ev)
23180     {
23181         var dom = ev.getTarget();
23182         //Roo.log(['leave',dom]);
23183         if (!this.currentEl) {
23184             return;
23185         }
23186         
23187         
23188         if (dom != this.currentEl.dom) {
23189             return;
23190         }
23191         var xy = ev.getXY();
23192         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23193             return;
23194         }
23195         // only activate leave if mouse cursor is outside... bounding box..
23196         
23197         
23198         
23199         
23200         if (this.currentTip) {
23201             this.currentTip.leave();
23202         }
23203         //Roo.log('clear currentEl');
23204         this.currentEl = false;
23205         
23206         
23207     },
23208     alignment : {
23209         'left' : ['r-l', [-2,0], 'right'],
23210         'right' : ['l-r', [2,0], 'left'],
23211         'bottom' : ['t-b', [0,2], 'top'],
23212         'top' : [ 'b-t', [0,-2], 'bottom']
23213     }
23214     
23215 });
23216
23217
23218 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23219     
23220     
23221     bindEl : false,
23222     
23223     delay : null, // can be { show : 300 , hide: 500}
23224     
23225     timeout : null,
23226     
23227     hoverState : null, //???
23228     
23229     placement : 'bottom', 
23230     
23231     getAutoCreate : function(){
23232     
23233         var cfg = {
23234            cls : 'tooltip',
23235            role : 'tooltip',
23236            cn : [
23237                 {
23238                     cls : 'tooltip-arrow'
23239                 },
23240                 {
23241                     cls : 'tooltip-inner'
23242                 }
23243            ]
23244         };
23245         
23246         return cfg;
23247     },
23248     bind : function(el)
23249     {
23250         this.bindEl = el;
23251     },
23252       
23253     
23254     enter : function () {
23255        
23256         if (this.timeout != null) {
23257             clearTimeout(this.timeout);
23258         }
23259         
23260         this.hoverState = 'in';
23261          //Roo.log("enter - show");
23262         if (!this.delay || !this.delay.show) {
23263             this.show();
23264             return;
23265         }
23266         var _t = this;
23267         this.timeout = setTimeout(function () {
23268             if (_t.hoverState == 'in') {
23269                 _t.show();
23270             }
23271         }, this.delay.show);
23272     },
23273     leave : function()
23274     {
23275         clearTimeout(this.timeout);
23276     
23277         this.hoverState = 'out';
23278          if (!this.delay || !this.delay.hide) {
23279             this.hide();
23280             return;
23281         }
23282        
23283         var _t = this;
23284         this.timeout = setTimeout(function () {
23285             //Roo.log("leave - timeout");
23286             
23287             if (_t.hoverState == 'out') {
23288                 _t.hide();
23289                 Roo.bootstrap.Tooltip.currentEl = false;
23290             }
23291         }, delay);
23292     },
23293     
23294     show : function ()
23295     {
23296         if (!this.el) {
23297             this.render(document.body);
23298         }
23299         // set content.
23300         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23301         
23302         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23303         
23304         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23305         
23306         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23307         
23308         var placement = typeof this.placement == 'function' ?
23309             this.placement.call(this, this.el, on_el) :
23310             this.placement;
23311             
23312         var autoToken = /\s?auto?\s?/i;
23313         var autoPlace = autoToken.test(placement);
23314         if (autoPlace) {
23315             placement = placement.replace(autoToken, '') || 'top';
23316         }
23317         
23318         //this.el.detach()
23319         //this.el.setXY([0,0]);
23320         this.el.show();
23321         //this.el.dom.style.display='block';
23322         this.el.addClass(placement);
23323         
23324         //this.el.appendTo(on_el);
23325         
23326         var p = this.getPosition();
23327         var box = this.el.getBox();
23328         
23329         if (autoPlace) {
23330             // fixme..
23331         }
23332         var align = Roo.bootstrap.Tooltip.alignment[placement];
23333         this.el.alignTo(this.bindEl, align[0],align[1]);
23334         //var arrow = this.el.select('.arrow',true).first();
23335         //arrow.set(align[2], 
23336         
23337         this.el.addClass('in fade');
23338         this.hoverState = null;
23339         
23340         if (this.el.hasClass('fade')) {
23341             // fade it?
23342         }
23343         
23344     },
23345     hide : function()
23346     {
23347          
23348         if (!this.el) {
23349             return;
23350         }
23351         //this.el.setXY([0,0]);
23352         this.el.removeClass('in');
23353         //this.el.hide();
23354         
23355     }
23356     
23357 });
23358  
23359
23360  /*
23361  * - LGPL
23362  *
23363  * Location Picker
23364  * 
23365  */
23366
23367 /**
23368  * @class Roo.bootstrap.LocationPicker
23369  * @extends Roo.bootstrap.Component
23370  * Bootstrap LocationPicker class
23371  * @cfg {Number} latitude Position when init default 0
23372  * @cfg {Number} longitude Position when init default 0
23373  * @cfg {Number} zoom default 15
23374  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23375  * @cfg {Boolean} mapTypeControl default false
23376  * @cfg {Boolean} disableDoubleClickZoom default false
23377  * @cfg {Boolean} scrollwheel default true
23378  * @cfg {Boolean} streetViewControl default false
23379  * @cfg {Number} radius default 0
23380  * @cfg {String} locationName
23381  * @cfg {Boolean} draggable default true
23382  * @cfg {Boolean} enableAutocomplete default false
23383  * @cfg {Boolean} enableReverseGeocode default true
23384  * @cfg {String} markerTitle
23385  * 
23386  * @constructor
23387  * Create a new LocationPicker
23388  * @param {Object} config The config object
23389  */
23390
23391
23392 Roo.bootstrap.LocationPicker = function(config){
23393     
23394     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23395     
23396     this.addEvents({
23397         /**
23398          * @event initial
23399          * Fires when the picker initialized.
23400          * @param {Roo.bootstrap.LocationPicker} this
23401          * @param {Google Location} location
23402          */
23403         initial : true,
23404         /**
23405          * @event positionchanged
23406          * Fires when the picker position changed.
23407          * @param {Roo.bootstrap.LocationPicker} this
23408          * @param {Google Location} location
23409          */
23410         positionchanged : true,
23411         /**
23412          * @event resize
23413          * Fires when the map resize.
23414          * @param {Roo.bootstrap.LocationPicker} this
23415          */
23416         resize : true,
23417         /**
23418          * @event show
23419          * Fires when the map show.
23420          * @param {Roo.bootstrap.LocationPicker} this
23421          */
23422         show : true,
23423         /**
23424          * @event hide
23425          * Fires when the map hide.
23426          * @param {Roo.bootstrap.LocationPicker} this
23427          */
23428         hide : true,
23429         /**
23430          * @event mapClick
23431          * Fires when click the map.
23432          * @param {Roo.bootstrap.LocationPicker} this
23433          * @param {Map event} e
23434          */
23435         mapClick : true,
23436         /**
23437          * @event mapRightClick
23438          * Fires when right click the map.
23439          * @param {Roo.bootstrap.LocationPicker} this
23440          * @param {Map event} e
23441          */
23442         mapRightClick : true,
23443         /**
23444          * @event markerClick
23445          * Fires when click the marker.
23446          * @param {Roo.bootstrap.LocationPicker} this
23447          * @param {Map event} e
23448          */
23449         markerClick : true,
23450         /**
23451          * @event markerRightClick
23452          * Fires when right click the marker.
23453          * @param {Roo.bootstrap.LocationPicker} this
23454          * @param {Map event} e
23455          */
23456         markerRightClick : true,
23457         /**
23458          * @event OverlayViewDraw
23459          * Fires when OverlayView Draw
23460          * @param {Roo.bootstrap.LocationPicker} this
23461          */
23462         OverlayViewDraw : true,
23463         /**
23464          * @event OverlayViewOnAdd
23465          * Fires when OverlayView Draw
23466          * @param {Roo.bootstrap.LocationPicker} this
23467          */
23468         OverlayViewOnAdd : true,
23469         /**
23470          * @event OverlayViewOnRemove
23471          * Fires when OverlayView Draw
23472          * @param {Roo.bootstrap.LocationPicker} this
23473          */
23474         OverlayViewOnRemove : true,
23475         /**
23476          * @event OverlayViewShow
23477          * Fires when OverlayView Draw
23478          * @param {Roo.bootstrap.LocationPicker} this
23479          * @param {Pixel} cpx
23480          */
23481         OverlayViewShow : true,
23482         /**
23483          * @event OverlayViewHide
23484          * Fires when OverlayView Draw
23485          * @param {Roo.bootstrap.LocationPicker} this
23486          */
23487         OverlayViewHide : true
23488     });
23489         
23490 };
23491
23492 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23493     
23494     gMapContext: false,
23495     
23496     latitude: 0,
23497     longitude: 0,
23498     zoom: 15,
23499     mapTypeId: false,
23500     mapTypeControl: false,
23501     disableDoubleClickZoom: false,
23502     scrollwheel: true,
23503     streetViewControl: false,
23504     radius: 0,
23505     locationName: '',
23506     draggable: true,
23507     enableAutocomplete: false,
23508     enableReverseGeocode: true,
23509     markerTitle: '',
23510     
23511     getAutoCreate: function()
23512     {
23513
23514         var cfg = {
23515             tag: 'div',
23516             cls: 'roo-location-picker'
23517         };
23518         
23519         return cfg
23520     },
23521     
23522     initEvents: function(ct, position)
23523     {       
23524         if(!this.el.getWidth() || this.isApplied()){
23525             return;
23526         }
23527         
23528         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23529         
23530         this.initial();
23531     },
23532     
23533     initial: function()
23534     {
23535         if(!this.mapTypeId){
23536             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23537         }
23538         
23539         this.gMapContext = this.GMapContext();
23540         
23541         this.initOverlayView();
23542         
23543         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23544         
23545         var _this = this;
23546                 
23547         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23548             _this.setPosition(_this.gMapContext.marker.position);
23549         });
23550         
23551         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23552             _this.fireEvent('mapClick', this, event);
23553             
23554         });
23555
23556         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23557             _this.fireEvent('mapRightClick', this, event);
23558             
23559         });
23560         
23561         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23562             _this.fireEvent('markerClick', this, event);
23563             
23564         });
23565
23566         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23567             _this.fireEvent('markerRightClick', this, event);
23568             
23569         });
23570         
23571         this.setPosition(this.gMapContext.location);
23572         
23573         this.fireEvent('initial', this, this.gMapContext.location);
23574     },
23575     
23576     initOverlayView: function()
23577     {
23578         var _this = this;
23579         
23580         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23581             
23582             draw: function()
23583             {
23584                 _this.fireEvent('OverlayViewDraw', _this);
23585             },
23586             
23587             onAdd: function()
23588             {
23589                 _this.fireEvent('OverlayViewOnAdd', _this);
23590             },
23591             
23592             onRemove: function()
23593             {
23594                 _this.fireEvent('OverlayViewOnRemove', _this);
23595             },
23596             
23597             show: function(cpx)
23598             {
23599                 _this.fireEvent('OverlayViewShow', _this, cpx);
23600             },
23601             
23602             hide: function()
23603             {
23604                 _this.fireEvent('OverlayViewHide', _this);
23605             }
23606             
23607         });
23608     },
23609     
23610     fromLatLngToContainerPixel: function(event)
23611     {
23612         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23613     },
23614     
23615     isApplied: function() 
23616     {
23617         return this.getGmapContext() == false ? false : true;
23618     },
23619     
23620     getGmapContext: function() 
23621     {
23622         return this.gMapContext
23623     },
23624     
23625     GMapContext: function() 
23626     {
23627         var position = new google.maps.LatLng(this.latitude, this.longitude);
23628         
23629         var _map = new google.maps.Map(this.el.dom, {
23630             center: position,
23631             zoom: this.zoom,
23632             mapTypeId: this.mapTypeId,
23633             mapTypeControl: this.mapTypeControl,
23634             disableDoubleClickZoom: this.disableDoubleClickZoom,
23635             scrollwheel: this.scrollwheel,
23636             streetViewControl: this.streetViewControl,
23637             locationName: this.locationName,
23638             draggable: this.draggable,
23639             enableAutocomplete: this.enableAutocomplete,
23640             enableReverseGeocode: this.enableReverseGeocode
23641         });
23642         
23643         var _marker = new google.maps.Marker({
23644             position: position,
23645             map: _map,
23646             title: this.markerTitle,
23647             draggable: this.draggable
23648         });
23649         
23650         return {
23651             map: _map,
23652             marker: _marker,
23653             circle: null,
23654             location: position,
23655             radius: this.radius,
23656             locationName: this.locationName,
23657             addressComponents: {
23658                 formatted_address: null,
23659                 addressLine1: null,
23660                 addressLine2: null,
23661                 streetName: null,
23662                 streetNumber: null,
23663                 city: null,
23664                 district: null,
23665                 state: null,
23666                 stateOrProvince: null
23667             },
23668             settings: this,
23669             domContainer: this.el.dom,
23670             geodecoder: new google.maps.Geocoder()
23671         };
23672     },
23673     
23674     drawCircle: function(center, radius, options) 
23675     {
23676         if (this.gMapContext.circle != null) {
23677             this.gMapContext.circle.setMap(null);
23678         }
23679         if (radius > 0) {
23680             radius *= 1;
23681             options = Roo.apply({}, options, {
23682                 strokeColor: "#0000FF",
23683                 strokeOpacity: .35,
23684                 strokeWeight: 2,
23685                 fillColor: "#0000FF",
23686                 fillOpacity: .2
23687             });
23688             
23689             options.map = this.gMapContext.map;
23690             options.radius = radius;
23691             options.center = center;
23692             this.gMapContext.circle = new google.maps.Circle(options);
23693             return this.gMapContext.circle;
23694         }
23695         
23696         return null;
23697     },
23698     
23699     setPosition: function(location) 
23700     {
23701         this.gMapContext.location = location;
23702         this.gMapContext.marker.setPosition(location);
23703         this.gMapContext.map.panTo(location);
23704         this.drawCircle(location, this.gMapContext.radius, {});
23705         
23706         var _this = this;
23707         
23708         if (this.gMapContext.settings.enableReverseGeocode) {
23709             this.gMapContext.geodecoder.geocode({
23710                 latLng: this.gMapContext.location
23711             }, function(results, status) {
23712                 
23713                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23714                     _this.gMapContext.locationName = results[0].formatted_address;
23715                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23716                     
23717                     _this.fireEvent('positionchanged', this, location);
23718                 }
23719             });
23720             
23721             return;
23722         }
23723         
23724         this.fireEvent('positionchanged', this, location);
23725     },
23726     
23727     resize: function()
23728     {
23729         google.maps.event.trigger(this.gMapContext.map, "resize");
23730         
23731         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23732         
23733         this.fireEvent('resize', this);
23734     },
23735     
23736     setPositionByLatLng: function(latitude, longitude)
23737     {
23738         this.setPosition(new google.maps.LatLng(latitude, longitude));
23739     },
23740     
23741     getCurrentPosition: function() 
23742     {
23743         return {
23744             latitude: this.gMapContext.location.lat(),
23745             longitude: this.gMapContext.location.lng()
23746         };
23747     },
23748     
23749     getAddressName: function() 
23750     {
23751         return this.gMapContext.locationName;
23752     },
23753     
23754     getAddressComponents: function() 
23755     {
23756         return this.gMapContext.addressComponents;
23757     },
23758     
23759     address_component_from_google_geocode: function(address_components) 
23760     {
23761         var result = {};
23762         
23763         for (var i = 0; i < address_components.length; i++) {
23764             var component = address_components[i];
23765             if (component.types.indexOf("postal_code") >= 0) {
23766                 result.postalCode = component.short_name;
23767             } else if (component.types.indexOf("street_number") >= 0) {
23768                 result.streetNumber = component.short_name;
23769             } else if (component.types.indexOf("route") >= 0) {
23770                 result.streetName = component.short_name;
23771             } else if (component.types.indexOf("neighborhood") >= 0) {
23772                 result.city = component.short_name;
23773             } else if (component.types.indexOf("locality") >= 0) {
23774                 result.city = component.short_name;
23775             } else if (component.types.indexOf("sublocality") >= 0) {
23776                 result.district = component.short_name;
23777             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23778                 result.stateOrProvince = component.short_name;
23779             } else if (component.types.indexOf("country") >= 0) {
23780                 result.country = component.short_name;
23781             }
23782         }
23783         
23784         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23785         result.addressLine2 = "";
23786         return result;
23787     },
23788     
23789     setZoomLevel: function(zoom)
23790     {
23791         this.gMapContext.map.setZoom(zoom);
23792     },
23793     
23794     show: function()
23795     {
23796         if(!this.el){
23797             return;
23798         }
23799         
23800         this.el.show();
23801         
23802         this.resize();
23803         
23804         this.fireEvent('show', this);
23805     },
23806     
23807     hide: function()
23808     {
23809         if(!this.el){
23810             return;
23811         }
23812         
23813         this.el.hide();
23814         
23815         this.fireEvent('hide', this);
23816     }
23817     
23818 });
23819
23820 Roo.apply(Roo.bootstrap.LocationPicker, {
23821     
23822     OverlayView : function(map, options)
23823     {
23824         options = options || {};
23825         
23826         this.setMap(map);
23827     }
23828     
23829     
23830 });/*
23831  * - LGPL
23832  *
23833  * Alert
23834  * 
23835  */
23836
23837 /**
23838  * @class Roo.bootstrap.Alert
23839  * @extends Roo.bootstrap.Component
23840  * Bootstrap Alert class
23841  * @cfg {String} title The title of alert
23842  * @cfg {String} html The content of alert
23843  * @cfg {String} weight (  success | info | warning | danger )
23844  * @cfg {String} faicon font-awesomeicon
23845  * 
23846  * @constructor
23847  * Create a new alert
23848  * @param {Object} config The config object
23849  */
23850
23851
23852 Roo.bootstrap.Alert = function(config){
23853     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23854     
23855 };
23856
23857 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23858     
23859     title: '',
23860     html: '',
23861     weight: false,
23862     faicon: false,
23863     
23864     getAutoCreate : function()
23865     {
23866         
23867         var cfg = {
23868             tag : 'div',
23869             cls : 'alert',
23870             cn : [
23871                 {
23872                     tag : 'i',
23873                     cls : 'roo-alert-icon'
23874                     
23875                 },
23876                 {
23877                     tag : 'b',
23878                     cls : 'roo-alert-title',
23879                     html : this.title
23880                 },
23881                 {
23882                     tag : 'span',
23883                     cls : 'roo-alert-text',
23884                     html : this.html
23885                 }
23886             ]
23887         };
23888         
23889         if(this.faicon){
23890             cfg.cn[0].cls += ' fa ' + this.faicon;
23891         }
23892         
23893         if(this.weight){
23894             cfg.cls += ' alert-' + this.weight;
23895         }
23896         
23897         return cfg;
23898     },
23899     
23900     initEvents: function() 
23901     {
23902         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23903     },
23904     
23905     setTitle : function(str)
23906     {
23907         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23908     },
23909     
23910     setText : function(str)
23911     {
23912         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23913     },
23914     
23915     setWeight : function(weight)
23916     {
23917         if(this.weight){
23918             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23919         }
23920         
23921         this.weight = weight;
23922         
23923         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23924     },
23925     
23926     setIcon : function(icon)
23927     {
23928         if(this.faicon){
23929             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23930         }
23931         
23932         this.faicon = icon
23933         
23934         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23935     },
23936     
23937     hide: function() 
23938     {
23939         this.el.hide();   
23940     },
23941     
23942     show: function() 
23943     {  
23944         this.el.show();   
23945     }
23946     
23947 });
23948
23949  
23950 /*
23951 * Licence: LGPL
23952 */
23953
23954 /**
23955  * @class Roo.bootstrap.UploadCropbox
23956  * @extends Roo.bootstrap.Component
23957  * Bootstrap UploadCropbox class
23958  * @cfg {String} emptyText show when image has been loaded
23959  * @cfg {String} rotateNotify show when image too small to rotate
23960  * @cfg {Number} errorTimeout default 3000
23961  * @cfg {Number} minWidth default 300
23962  * @cfg {Number} minHeight default 300
23963  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23964  * 
23965  * @constructor
23966  * Create a new UploadCropbox
23967  * @param {Object} config The config object
23968  */
23969
23970 Roo.bootstrap.UploadCropbox = function(config){
23971     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23972     
23973     this.addEvents({
23974         /**
23975          * @event beforeselectfile
23976          * Fire before select file
23977          * @param {Roo.bootstrap.UploadCropbox} this
23978          */
23979         "beforeselectfile" : true,
23980         /**
23981          * @event initial
23982          * Fire after initEvent
23983          * @param {Roo.bootstrap.UploadCropbox} this
23984          */
23985         "initial" : true,
23986         /**
23987          * @event crop
23988          * Fire after initEvent
23989          * @param {Roo.bootstrap.UploadCropbox} this
23990          * @param {String} data
23991          */
23992         "crop" : true,
23993         /**
23994          * @event prepare
23995          * Fire when preparing the file data
23996          * @param {Roo.bootstrap.UploadCropbox} this
23997          * @param {Object} file
23998          */
23999         "prepare" : true,
24000         /**
24001          * @event exception
24002          * Fire when get exception
24003          * @param {Roo.bootstrap.UploadCropbox} this
24004          * @param {Object} options
24005          */
24006         "exception" : true,
24007         /**
24008          * @event beforeloadcanvas
24009          * Fire before load the canvas
24010          * @param {Roo.bootstrap.UploadCropbox} this
24011          * @param {String} src
24012          */
24013         "beforeloadcanvas" : true,
24014         /**
24015          * @event trash
24016          * Fire when trash image
24017          * @param {Roo.bootstrap.UploadCropbox} this
24018          */
24019         "trash" : true,
24020         /**
24021          * @event download
24022          * Fire when download the image
24023          * @param {Roo.bootstrap.UploadCropbox} this
24024          */
24025         "download" : true,
24026         /**
24027          * @event footerbuttonclick
24028          * Fire when footerbuttonclick
24029          * @param {Roo.bootstrap.UploadCropbox} this
24030          * @param {String} type
24031          */
24032         "footerbuttonclick" : true,
24033         /**
24034          * @event resize
24035          * Fire when resize
24036          * @param {Roo.bootstrap.UploadCropbox} this
24037          */
24038         "resize" : true
24039         
24040     });
24041     
24042     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24043 };
24044
24045 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24046     
24047     emptyText : 'Click to upload image',
24048     rotateNotify : 'Image is too small to rotate',
24049     errorTimeout : 3000,
24050     scale : 0,
24051     baseScale : 1,
24052     rotate : 0,
24053     dragable : false,
24054     pinching : false,
24055     mouseX : 0,
24056     mouseY : 0,
24057     cropData : false,
24058     minWidth : 300,
24059     minHeight : 300,
24060     file : false,
24061     exif : {},
24062     baseRotate : 1,
24063     cropType : 'image/jpeg',
24064     buttons : false,
24065     canvasLoaded : false,
24066     
24067     getAutoCreate : function()
24068     {
24069         var cfg = {
24070             tag : 'div',
24071             cls : 'roo-upload-cropbox',
24072             cn : [
24073                 {
24074                     tag : 'div',
24075                     cls : 'roo-upload-cropbox-body',
24076                     style : 'cursor:pointer',
24077                     cn : [
24078                         {
24079                             tag : 'div',
24080                             cls : 'roo-upload-cropbox-preview'
24081                         },
24082                         {
24083                             tag : 'div',
24084                             cls : 'roo-upload-cropbox-thumb'
24085                         },
24086                         {
24087                             tag : 'div',
24088                             cls : 'roo-upload-cropbox-empty-notify',
24089                             html : this.emptyText
24090                         },
24091                         {
24092                             tag : 'div',
24093                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24094                             html : this.rotateNotify
24095                         }
24096                     ]
24097                 },
24098                 {
24099                     tag : 'div',
24100                     cls : 'roo-upload-cropbox-footer',
24101                     cn : {
24102                         tag : 'div',
24103                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24104                         cn : []
24105                     }
24106                 }
24107             ]
24108         };
24109         
24110         return cfg;
24111     },
24112     
24113     onRender : function(ct, position)
24114     {
24115         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24116         
24117         if (this.buttons.length) {
24118             
24119             Roo.each(this.buttons, function(bb) {
24120                 
24121                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24122                 
24123                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24124                 
24125             }, this);
24126         }
24127     },
24128     
24129     initEvents : function()
24130     {
24131         this.urlAPI = (window.createObjectURL && window) || 
24132                                 (window.URL && URL.revokeObjectURL && URL) || 
24133                                 (window.webkitURL && webkitURL);
24134                         
24135         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24136         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24137         
24138         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24139         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24140         
24141         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24142         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24143         this.thumbEl.hide();
24144         
24145         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24146         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24147         
24148         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24149         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24150         this.errorEl.hide();
24151         
24152         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24153         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24154         this.footerEl.hide();
24155         
24156         this.setThumbBoxSize();
24157         
24158         this.bind();
24159         
24160         this.resize();
24161         
24162         this.fireEvent('initial', this);
24163     },
24164
24165     bind : function()
24166     {
24167         var _this = this;
24168         
24169         window.addEventListener("resize", function() { _this.resize(); } );
24170         
24171         this.bodyEl.on('click', this.beforeSelectFile, this);
24172         
24173         if(Roo.isTouch){
24174             this.bodyEl.on('touchstart', this.onTouchStart, this);
24175             this.bodyEl.on('touchmove', this.onTouchMove, this);
24176             this.bodyEl.on('touchend', this.onTouchEnd, this);
24177         }
24178         
24179         if(!Roo.isTouch){
24180             this.bodyEl.on('mousedown', this.onMouseDown, this);
24181             this.bodyEl.on('mousemove', this.onMouseMove, this);
24182             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24183             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24184             Roo.get(document).on('mouseup', this.onMouseUp, this);
24185         }
24186     },
24187     
24188     reset : function()
24189     {    
24190         this.scale = 0;
24191         this.baseScale = 1;
24192         this.rotate = 0;
24193         this.baseRotate = 1;
24194         this.dragable = false;
24195         this.pinching = false;
24196         this.mouseX = 0;
24197         this.mouseY = 0;
24198         this.cropData = false;
24199         this.notifyEl.dom.innerHTML = this.emptyText;
24200         
24201     },
24202     
24203     resize : function()
24204     {
24205         if(this.fireEvent('resize', this) != false){
24206             this.setThumbBoxPosition();
24207             this.setCanvasPosition();
24208         }
24209     },
24210     
24211     onFooterButtonClick : function(e, el, o, type)
24212     {
24213         switch (type) {
24214             case 'rotate-left' :
24215                 this.onRotateLeft(e);
24216                 break;
24217             case 'rotate-right' :
24218                 this.onRotateRight(e);
24219                 break;
24220             case 'picture' :
24221                 this.beforeSelectFile(e);
24222                 break;
24223             case 'trash' :
24224                 this.trash(e);
24225                 break;
24226             case 'crop' :
24227                 this.crop(e);
24228                 break;
24229             case 'download' :
24230                 this.download(e);
24231                 break;
24232             default :
24233                 break;
24234         }
24235         
24236         this.fireEvent('footerbuttonclick', this, type);
24237     },
24238     
24239     beforeSelectFile : function(e)
24240     {
24241         this.fireEvent('beforeselectfile', this);
24242     },
24243     
24244     trash : function(e)
24245     {
24246         this.fireEvent('trash', this);
24247     },
24248     
24249     download : function(e)
24250     {
24251         this.fireEvent('download', this);
24252     },
24253     
24254     loadCanvas : function(src)
24255     {   
24256         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24257             
24258             this.reset();
24259             
24260             this.imageEl = document.createElement('img');
24261             
24262             var _this = this;
24263             
24264             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24265             
24266             this.imageEl.src = src;
24267         }
24268     },
24269     
24270     onLoadCanvas : function()
24271     {   
24272         this.bodyEl.un('click', this.beforeSelectFile, this);
24273         
24274         this.notifyEl.hide();
24275         this.thumbEl.show();
24276         this.footerEl.show();
24277         
24278         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24279         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24280         
24281         this.setThumbBoxPosition();
24282         this.baseRotateLevel();
24283         this.baseScaleLevel();
24284         
24285         this.draw();
24286         
24287         this.resize();
24288         
24289         this.canvasLoaded = true;
24290         
24291     },
24292     
24293     setCanvasPosition : function()
24294     {   
24295         if(!this.canvasEl){
24296             return;
24297         }
24298         
24299         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24300         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24301         
24302         this.previewEl.setLeft(pw);
24303         this.previewEl.setTop(ph);
24304         
24305     },
24306     
24307     onMouseDown : function(e)
24308     {   
24309         e.stopEvent();
24310         
24311         this.dragable = true;
24312         this.pinching = false;
24313         
24314         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24315         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24316         
24317     },
24318     
24319     onMouseMove : function(e)
24320     {   
24321         e.stopEvent();
24322         
24323         if(!this.canvasLoaded){
24324             return;
24325         }
24326         
24327         if (!this.dragable){
24328             return;
24329         }
24330         
24331         var minX = Math.ceil(this.thumbEl.getLeft(true));
24332         var minY = Math.ceil(this.thumbEl.getTop(true));
24333         
24334         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24335         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24336         
24337         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24338         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24339         
24340         x = x - this.mouseX;
24341         y = y - this.mouseY;
24342         
24343         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24344         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24345         
24346         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24347         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24348         
24349         this.previewEl.setLeft(bgX);
24350         this.previewEl.setTop(bgY);
24351         
24352         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24353         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24354     },
24355     
24356     onMouseUp : function(e)
24357     {   
24358         e.stopEvent();
24359         
24360         this.dragable = false;
24361     },
24362     
24363     onMouseWheel : function(e)
24364     {   
24365         e.stopEvent();
24366         
24367         this.startScale = this.scale;
24368         
24369         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24370         
24371         if(!this.zoomable()){
24372             this.scale = this.startScale;
24373             return;
24374         }
24375         
24376         this.draw();
24377         
24378         return;
24379     },
24380     
24381     zoomable : function()
24382     {
24383         var minScale = this.thumbEl.getWidth() / this.minWidth;
24384         
24385         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24386         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24387         
24388         if(
24389                 (this.rotate == 0 || this.rotate == 180) && 
24390                 (
24391                     width / minScale < this.minWidth || 
24392                     width / minScale > this.imageEl.OriginWidth || 
24393                     height / minScale < this.minHeight || 
24394                     height / minScale > this.imageEl.OriginHeight
24395                 )
24396         ){
24397             return false;
24398         }
24399         
24400         if(
24401                 (this.rotate == 90 || this.rotate == 270) && 
24402                 (
24403                     width / minScale < this.minHeight || 
24404                     width / minScale > this.imageEl.OriginWidth || 
24405                     height / minScale < this.minWidth || 
24406                     height / minScale > this.imageEl.OriginHeight
24407                 )
24408         ){
24409             return false;
24410         }
24411         
24412         return true;
24413         
24414     },
24415     
24416     onRotateLeft : function(e)
24417     {   
24418         var minScale = this.thumbEl.getWidth() / this.minWidth;
24419         
24420         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24421             
24422             var bw = this.canvasEl.width / this.getScaleLevel();
24423             var bh = this.canvasEl.height / this.getScaleLevel();
24424             
24425             this.startScale = this.scale;
24426             
24427             while (this.getScaleLevel() < minScale){
24428             
24429                 this.scale = this.scale + 1;
24430                 
24431                 if(!this.zoomable()){
24432                     break;
24433                 }
24434                 
24435                 if(
24436                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24437                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
24438                 ){
24439                     continue;
24440                 }
24441                 
24442                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24443
24444                 this.draw();
24445                 
24446                 return;
24447             }
24448             
24449             this.scale = this.startScale;
24450             
24451             this.onRotateFail();
24452             
24453             return false;
24454         }
24455         
24456         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24457
24458         this.draw();
24459         
24460     },
24461     
24462     onRotateRight : function(e)
24463     {
24464         var minScale = this.thumbEl.getWidth() / this.minWidth;
24465         
24466         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24467             
24468             var bw = this.canvasEl.width / this.getScaleLevel();
24469             var bh = this.canvasEl.height / this.getScaleLevel();
24470             
24471             this.startScale = this.scale;
24472             
24473             while (this.getScaleLevel() < minScale){
24474             
24475                 this.scale = this.scale + 1;
24476                 
24477                 if(!this.zoomable()){
24478                     break;
24479                 }
24480                 
24481                 if(
24482                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24483                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
24484                 ){
24485                     continue;
24486                 }
24487                 
24488                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24489
24490                 this.draw();
24491                 
24492                 return;
24493             }
24494             
24495             this.scale = this.startScale;
24496             
24497             this.onRotateFail();
24498             
24499             return false;
24500         }
24501         
24502         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24503
24504         this.draw();
24505     },
24506     
24507     onRotateFail : function()
24508     {
24509         this.errorEl.show(true);
24510         
24511         var _this = this;
24512         
24513         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24514     },
24515     
24516     draw : function()
24517     {
24518         this.previewEl.dom.innerHTML = '';
24519         
24520         var canvasEl = document.createElement("canvas");
24521         
24522         var contextEl = canvasEl.getContext("2d");
24523         
24524         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24525         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24526         var center = this.imageEl.OriginWidth / 2;
24527         
24528         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24529             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24530             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24531             center = this.imageEl.OriginHeight / 2;
24532         }
24533         
24534         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24535         
24536         contextEl.translate(center, center);
24537         contextEl.rotate(this.rotate * Math.PI / 180);
24538
24539         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24540         
24541         this.canvasEl = document.createElement("canvas");
24542         
24543         this.contextEl = this.canvasEl.getContext("2d");
24544         
24545         switch (this.rotate) {
24546             case 0 :
24547                 
24548                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24549                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24550                 
24551                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24552                 
24553                 break;
24554             case 90 : 
24555                 
24556                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24557                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24558                 
24559                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24560                     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);
24561                     break;
24562                 }
24563                 
24564                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24565                 
24566                 break;
24567             case 180 :
24568                 
24569                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24570                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24571                 
24572                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24573                     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);
24574                     break;
24575                 }
24576                 
24577                 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);
24578                 
24579                 break;
24580             case 270 :
24581                 
24582                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24583                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24584         
24585                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24586                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24587                     break;
24588                 }
24589                 
24590                 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);
24591                 
24592                 break;
24593             default : 
24594                 break;
24595         }
24596         
24597         this.previewEl.appendChild(this.canvasEl);
24598         
24599         this.setCanvasPosition();
24600     },
24601     
24602     crop : function()
24603     {
24604         if(!this.canvasLoaded){
24605             return;
24606         }
24607         var canvas = document.createElement("canvas");
24608         
24609         var context = canvas.getContext("2d");
24610         
24611         canvas.width = this.minWidth;
24612         canvas.height = this.minHeight;
24613         
24614         var cropWidth = this.thumbEl.getWidth();
24615         var cropHeight = this.thumbEl.getHeight();
24616         
24617         var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24618         var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24619         
24620         if(this.canvasEl.width - cropWidth < x){
24621             x = this.canvasEl.width - cropWidth;
24622         }
24623         
24624         if(this.canvasEl.height - cropHeight < y){
24625             y = this.canvasEl.height - cropHeight;
24626         }
24627         
24628         x = x < 0 ? 0 : x;
24629         y = y < 0 ? 0 : y;
24630         
24631         context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24632         
24633         this.cropData = canvas.toDataURL(this.cropType);
24634         
24635         this.fireEvent('crop', this, this.cropData);
24636         
24637     },
24638     
24639     setThumbBoxSize : function()
24640     {
24641         var height = 300;
24642         var width = Math.ceil(this.minWidth * height / this.minHeight);
24643         
24644         if(this.minWidth > this.minHeight){
24645             width = 300;
24646             height = Math.ceil(this.minHeight * width / this.minWidth);
24647         }
24648         
24649         this.thumbEl.setStyle({
24650             width : width + 'px',
24651             height : height + 'px'
24652         });
24653
24654         return;
24655             
24656     },
24657     
24658     setThumbBoxPosition : function()
24659     {
24660         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24661         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24662         
24663         this.thumbEl.setLeft(x);
24664         this.thumbEl.setTop(y);
24665         
24666     },
24667     
24668     baseRotateLevel : function()
24669     {
24670         this.baseRotate = 1;
24671         
24672         if(
24673                 typeof(this.exif) != 'undefined' &&
24674                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24675                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24676         ){
24677             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24678         }
24679         
24680         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24681         
24682     },
24683     
24684     baseScaleLevel : function()
24685     {
24686         var width, height;
24687         
24688         if(this.baseRotate == 6 || this.baseRotate == 8){
24689             
24690             width = this.thumbEl.getHeight();
24691             this.baseScale = height / this.imageEl.OriginHeight;
24692             
24693             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24694                 height = this.thumbEl.getWidth();
24695                 this.baseScale = height / this.imageEl.OriginHeight;
24696             }
24697             
24698             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24699                 height = this.thumbEl.getWidth();
24700                 this.baseScale = height / this.imageEl.OriginHeight;
24701                 
24702                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24703                     width = this.thumbEl.getHeight();
24704                     this.baseScale = width / this.imageEl.OriginWidth;
24705                 }
24706             }
24707             
24708             return;
24709         }
24710         
24711         width = this.thumbEl.getWidth();
24712         this.baseScale = width / this.imageEl.OriginWidth;
24713         
24714         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24715             height = this.thumbEl.getHeight();
24716             this.baseScale = height / this.imageEl.OriginHeight;
24717         }
24718         
24719         
24720         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24721             
24722             height = this.thumbEl.getHeight();
24723             this.baseScale = height / this.imageEl.OriginHeight;
24724             
24725             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24726                 width = this.thumbEl.getWidth();
24727                 this.baseScale = width / this.imageEl.OriginWidth;
24728             }
24729             
24730         }
24731         
24732         return;
24733     },
24734     
24735     getScaleLevel : function()
24736     {
24737         return this.baseScale * Math.pow(1.1, this.scale);
24738     },
24739     
24740     onTouchStart : function(e)
24741     {
24742         if(!this.canvasLoaded){
24743             this.beforeSelectFile(e);
24744             return;
24745         }
24746         
24747         var touches = e.browserEvent.touches;
24748         
24749         if(!touches){
24750             return;
24751         }
24752         
24753         if(touches.length == 1){
24754             this.onMouseDown(e);
24755             return;
24756         }
24757         
24758         if(touches.length != 2){
24759             return;
24760         }
24761         
24762         var coords = [];
24763         
24764         for(var i = 0, finger; finger = touches[i]; i++){
24765             coords.push(finger.pageX, finger.pageY);
24766         }
24767         
24768         var x = Math.pow(coords[0] - coords[2], 2);
24769         var y = Math.pow(coords[1] - coords[3], 2);
24770         
24771         this.startDistance = Math.sqrt(x + y);
24772         
24773         this.startScale = this.scale;
24774         
24775         this.pinching = true;
24776         this.dragable = false;
24777         
24778     },
24779     
24780     onTouchMove : function(e)
24781     {
24782         if(!this.pinching && !this.dragable){
24783             return;
24784         }
24785         
24786         var touches = e.browserEvent.touches;
24787         
24788         if(!touches){
24789             return;
24790         }
24791         
24792         if(this.dragable){
24793             this.onMouseMove(e);
24794             return;
24795         }
24796         
24797         var coords = [];
24798         
24799         for(var i = 0, finger; finger = touches[i]; i++){
24800             coords.push(finger.pageX, finger.pageY);
24801         }
24802         
24803         var x = Math.pow(coords[0] - coords[2], 2);
24804         var y = Math.pow(coords[1] - coords[3], 2);
24805         
24806         this.endDistance = Math.sqrt(x + y);
24807         
24808         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24809         
24810         if(!this.zoomable()){
24811             this.scale = this.startScale;
24812             return;
24813         }
24814         
24815         this.draw();
24816         
24817     },
24818     
24819     onTouchEnd : function(e)
24820     {
24821         this.pinching = false;
24822         this.dragable = false;
24823         
24824     },
24825     
24826     prepare : function(input)
24827     {        
24828         this.file = false;
24829         this.exif = {};
24830         
24831         if(typeof(input) === 'string'){
24832             this.loadCanvas(input);
24833             return;
24834         }
24835         
24836         if(!input.files || !input.files[0] || !this.urlAPI){
24837             return;
24838         }
24839         
24840         this.file = input.files[0];
24841         this.cropType = this.file.type;
24842         
24843         var _this = this;
24844         
24845         if(this.fireEvent('prepare', this, this.file) != false){
24846             
24847             var reader = new FileReader();
24848             
24849             reader.onload = function (e) {
24850                 if (e.target.error) {
24851                     Roo.log(e.target.error);
24852                     return;
24853                 }
24854                 
24855                 var buffer = e.target.result,
24856                     dataView = new DataView(buffer),
24857                     offset = 2,
24858                     maxOffset = dataView.byteLength - 4,
24859                     markerBytes,
24860                     markerLength;
24861                 
24862                 if (dataView.getUint16(0) === 0xffd8) {
24863                     while (offset < maxOffset) {
24864                         markerBytes = dataView.getUint16(offset);
24865                         
24866                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24867                             markerLength = dataView.getUint16(offset + 2) + 2;
24868                             if (offset + markerLength > dataView.byteLength) {
24869                                 Roo.log('Invalid meta data: Invalid segment size.');
24870                                 break;
24871                             }
24872                             
24873                             if(markerBytes == 0xffe1){
24874                                 _this.parseExifData(
24875                                     dataView,
24876                                     offset,
24877                                     markerLength
24878                                 );
24879                             }
24880                             
24881                             offset += markerLength;
24882                             
24883                             continue;
24884                         }
24885                         
24886                         break;
24887                     }
24888                     
24889                 }
24890                 
24891                 var url = _this.urlAPI.createObjectURL(_this.file);
24892                 
24893                 _this.loadCanvas(url);
24894                 
24895                 return;
24896             }
24897             
24898             reader.readAsArrayBuffer(this.file);
24899             
24900         }
24901         
24902     },
24903     
24904     parseExifData : function(dataView, offset, length)
24905     {
24906         var tiffOffset = offset + 10,
24907             littleEndian,
24908             dirOffset;
24909     
24910         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24911             // No Exif data, might be XMP data instead
24912             return;
24913         }
24914         
24915         // Check for the ASCII code for "Exif" (0x45786966):
24916         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24917             // No Exif data, might be XMP data instead
24918             return;
24919         }
24920         if (tiffOffset + 8 > dataView.byteLength) {
24921             Roo.log('Invalid Exif data: Invalid segment size.');
24922             return;
24923         }
24924         // Check for the two null bytes:
24925         if (dataView.getUint16(offset + 8) !== 0x0000) {
24926             Roo.log('Invalid Exif data: Missing byte alignment offset.');
24927             return;
24928         }
24929         // Check the byte alignment:
24930         switch (dataView.getUint16(tiffOffset)) {
24931         case 0x4949:
24932             littleEndian = true;
24933             break;
24934         case 0x4D4D:
24935             littleEndian = false;
24936             break;
24937         default:
24938             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24939             return;
24940         }
24941         // Check for the TIFF tag marker (0x002A):
24942         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24943             Roo.log('Invalid Exif data: Missing TIFF marker.');
24944             return;
24945         }
24946         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24947         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24948         
24949         this.parseExifTags(
24950             dataView,
24951             tiffOffset,
24952             tiffOffset + dirOffset,
24953             littleEndian
24954         );
24955     },
24956     
24957     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24958     {
24959         var tagsNumber,
24960             dirEndOffset,
24961             i;
24962         if (dirOffset + 6 > dataView.byteLength) {
24963             Roo.log('Invalid Exif data: Invalid directory offset.');
24964             return;
24965         }
24966         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24967         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24968         if (dirEndOffset + 4 > dataView.byteLength) {
24969             Roo.log('Invalid Exif data: Invalid directory size.');
24970             return;
24971         }
24972         for (i = 0; i < tagsNumber; i += 1) {
24973             this.parseExifTag(
24974                 dataView,
24975                 tiffOffset,
24976                 dirOffset + 2 + 12 * i, // tag offset
24977                 littleEndian
24978             );
24979         }
24980         // Return the offset to the next directory:
24981         return dataView.getUint32(dirEndOffset, littleEndian);
24982     },
24983     
24984     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
24985     {
24986         var tag = dataView.getUint16(offset, littleEndian);
24987         
24988         this.exif[tag] = this.getExifValue(
24989             dataView,
24990             tiffOffset,
24991             offset,
24992             dataView.getUint16(offset + 2, littleEndian), // tag type
24993             dataView.getUint32(offset + 4, littleEndian), // tag length
24994             littleEndian
24995         );
24996     },
24997     
24998     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
24999     {
25000         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25001             tagSize,
25002             dataOffset,
25003             values,
25004             i,
25005             str,
25006             c;
25007     
25008         if (!tagType) {
25009             Roo.log('Invalid Exif data: Invalid tag type.');
25010             return;
25011         }
25012         
25013         tagSize = tagType.size * length;
25014         // Determine if the value is contained in the dataOffset bytes,
25015         // or if the value at the dataOffset is a pointer to the actual data:
25016         dataOffset = tagSize > 4 ?
25017                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25018         if (dataOffset + tagSize > dataView.byteLength) {
25019             Roo.log('Invalid Exif data: Invalid data offset.');
25020             return;
25021         }
25022         if (length === 1) {
25023             return tagType.getValue(dataView, dataOffset, littleEndian);
25024         }
25025         values = [];
25026         for (i = 0; i < length; i += 1) {
25027             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25028         }
25029         
25030         if (tagType.ascii) {
25031             str = '';
25032             // Concatenate the chars:
25033             for (i = 0; i < values.length; i += 1) {
25034                 c = values[i];
25035                 // Ignore the terminating NULL byte(s):
25036                 if (c === '\u0000') {
25037                     break;
25038                 }
25039                 str += c;
25040             }
25041             return str;
25042         }
25043         return values;
25044     }
25045     
25046 });
25047
25048 Roo.apply(Roo.bootstrap.UploadCropbox, {
25049     tags : {
25050         'Orientation': 0x0112
25051     },
25052     
25053     Orientation: {
25054             1: 0, //'top-left',
25055 //            2: 'top-right',
25056             3: 180, //'bottom-right',
25057 //            4: 'bottom-left',
25058 //            5: 'left-top',
25059             6: 90, //'right-top',
25060 //            7: 'right-bottom',
25061             8: 270 //'left-bottom'
25062     },
25063     
25064     exifTagTypes : {
25065         // byte, 8-bit unsigned int:
25066         1: {
25067             getValue: function (dataView, dataOffset) {
25068                 return dataView.getUint8(dataOffset);
25069             },
25070             size: 1
25071         },
25072         // ascii, 8-bit byte:
25073         2: {
25074             getValue: function (dataView, dataOffset) {
25075                 return String.fromCharCode(dataView.getUint8(dataOffset));
25076             },
25077             size: 1,
25078             ascii: true
25079         },
25080         // short, 16 bit int:
25081         3: {
25082             getValue: function (dataView, dataOffset, littleEndian) {
25083                 return dataView.getUint16(dataOffset, littleEndian);
25084             },
25085             size: 2
25086         },
25087         // long, 32 bit int:
25088         4: {
25089             getValue: function (dataView, dataOffset, littleEndian) {
25090                 return dataView.getUint32(dataOffset, littleEndian);
25091             },
25092             size: 4
25093         },
25094         // rational = two long values, first is numerator, second is denominator:
25095         5: {
25096             getValue: function (dataView, dataOffset, littleEndian) {
25097                 return dataView.getUint32(dataOffset, littleEndian) /
25098                     dataView.getUint32(dataOffset + 4, littleEndian);
25099             },
25100             size: 8
25101         },
25102         // slong, 32 bit signed int:
25103         9: {
25104             getValue: function (dataView, dataOffset, littleEndian) {
25105                 return dataView.getInt32(dataOffset, littleEndian);
25106             },
25107             size: 4
25108         },
25109         // srational, two slongs, first is numerator, second is denominator:
25110         10: {
25111             getValue: function (dataView, dataOffset, littleEndian) {
25112                 return dataView.getInt32(dataOffset, littleEndian) /
25113                     dataView.getInt32(dataOffset + 4, littleEndian);
25114             },
25115             size: 8
25116         }
25117     },
25118     
25119     footer : {
25120         STANDARD : [
25121             {
25122                 tag : 'div',
25123                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25124                 action : 'rotate-left',
25125                 cn : [
25126                     {
25127                         tag : 'button',
25128                         cls : 'btn btn-default',
25129                         html : '<i class="fa fa-undo"></i>'
25130                     }
25131                 ]
25132             },
25133             {
25134                 tag : 'div',
25135                 cls : 'btn-group roo-upload-cropbox-picture',
25136                 action : 'picture',
25137                 cn : [
25138                     {
25139                         tag : 'button',
25140                         cls : 'btn btn-default',
25141                         html : '<i class="fa fa-picture-o"></i>'
25142                     }
25143                 ]
25144             },
25145             {
25146                 tag : 'div',
25147                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25148                 action : 'rotate-right',
25149                 cn : [
25150                     {
25151                         tag : 'button',
25152                         cls : 'btn btn-default',
25153                         html : '<i class="fa fa-repeat"></i>'
25154                     }
25155                 ]
25156             }
25157         ],
25158         DOCUMENT : [
25159             {
25160                 tag : 'div',
25161                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25162                 action : 'rotate-left',
25163                 cn : [
25164                     {
25165                         tag : 'button',
25166                         cls : 'btn btn-default',
25167                         html : '<i class="fa fa-undo"></i>'
25168                     }
25169                 ]
25170             },
25171             {
25172                 tag : 'div',
25173                 cls : 'btn-group roo-upload-cropbox-download',
25174                 action : 'download',
25175                 cn : [
25176                     {
25177                         tag : 'button',
25178                         cls : 'btn btn-default',
25179                         html : '<i class="fa fa-download"></i>'
25180                     }
25181                 ]
25182             },
25183             {
25184                 tag : 'div',
25185                 cls : 'btn-group roo-upload-cropbox-crop',
25186                 action : 'crop',
25187                 cn : [
25188                     {
25189                         tag : 'button',
25190                         cls : 'btn btn-default',
25191                         html : '<i class="fa fa-crop"></i>'
25192                     }
25193                 ]
25194             },
25195             {
25196                 tag : 'div',
25197                 cls : 'btn-group roo-upload-cropbox-trash',
25198                 action : 'trash',
25199                 cn : [
25200                     {
25201                         tag : 'button',
25202                         cls : 'btn btn-default',
25203                         html : '<i class="fa fa-trash"></i>'
25204                     }
25205                 ]
25206             },
25207             {
25208                 tag : 'div',
25209                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25210                 action : 'rotate-right',
25211                 cn : [
25212                     {
25213                         tag : 'button',
25214                         cls : 'btn btn-default',
25215                         html : '<i class="fa fa-repeat"></i>'
25216                     }
25217                 ]
25218             }
25219         ]
25220     }
25221 });
25222
25223 /*
25224 * Licence: LGPL
25225 */
25226
25227 /**
25228  * @class Roo.bootstrap.DocumentManager
25229  * @extends Roo.bootstrap.Component
25230  * Bootstrap DocumentManager class
25231  * @cfg {String} paramName default 'imageUpload'
25232  * @cfg {String} method default POST
25233  * @cfg {String} url action url
25234  * @cfg {Number} boxes number of boxes default 12
25235  * @cfg {Boolean} multiple multiple upload default true
25236  * @cfg {Number} minWidth default 300
25237  * @cfg {Number} minHeight default 300
25238  * @cfg {Number} thumbSize default 300
25239  * @cfg {String} fieldLabel
25240  * @cfg {Number} labelWidth default 4
25241  * @cfg {String} labelAlign (left|top) default left
25242  * 
25243  * @constructor
25244  * Create a new DocumentManager
25245  * @param {Object} config The config object
25246  */
25247
25248 Roo.bootstrap.DocumentManager = function(config){
25249     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25250     
25251     this.addEvents({
25252         /**
25253          * @event initial
25254          * Fire when initial the DocumentManager
25255          * @param {Roo.bootstrap.DocumentManager} this
25256          */
25257         "initial" : true,
25258         /**
25259          * @event inspect
25260          * inspect selected file
25261          * @param {Roo.bootstrap.DocumentManager} this
25262          * @param {File} file
25263          */
25264         "inspect" : true,
25265         /**
25266          * @event exception
25267          * Fire when xhr load exception
25268          * @param {Roo.bootstrap.DocumentManager} this
25269          * @param {XMLHttpRequest} xhr
25270          */
25271         "exception" : true,
25272         /**
25273          * @event prepare
25274          * prepare the form data
25275          * @param {Roo.bootstrap.DocumentManager} this
25276          * @param {Object} formData
25277          */
25278         "prepare" : true,
25279         /**
25280          * @event remove
25281          * Fire when remove the file
25282          * @param {Roo.bootstrap.DocumentManager} this
25283          * @param {Object} file
25284          */
25285         "remove" : true,
25286         /**
25287          * @event refresh
25288          * Fire after refresh the file
25289          * @param {Roo.bootstrap.DocumentManager} this
25290          */
25291         "refresh" : true,
25292         /**
25293          * @event click
25294          * Fire after click the image
25295          * @param {Roo.bootstrap.DocumentManager} this
25296          * @param {Object} file
25297          */
25298         "click" : true
25299         
25300     });
25301 };
25302
25303 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25304     
25305     boxes : 12,
25306     inputName : '',
25307     minWidth : 300,
25308     minHeight : 300,
25309     thumbSize : 300,
25310     multiple : true,
25311     files : [],
25312     method : 'POST',
25313     url : '',
25314     paramName : 'imageUpload',
25315     fieldLabel : '',
25316     labelWidth : 4,
25317     labelAlign : 'left',
25318     
25319     getAutoCreate : function()
25320     {   
25321         var managerWidget = {
25322             tag : 'div',
25323             cls : 'roo-document-manager',
25324             cn : [
25325                 {
25326                     tag : 'input',
25327                     cls : 'roo-document-manager-selector',
25328                     type : 'file'
25329                 },
25330                 {
25331                     tag : 'div',
25332                     cls : 'roo-document-manager-uploader',
25333                     cn : [
25334                         {
25335                             tag : 'div',
25336                             cls : 'roo-document-manager-upload-btn',
25337                             html : '<i class="fa fa-plus"></i>'
25338                         }
25339                     ]
25340                     
25341                 }
25342             ]
25343         };
25344         
25345         var content = [
25346             {
25347                 tag : 'div',
25348                 cls : 'column col-md-12',
25349                 cn : managerWidget
25350             }
25351         ];
25352         
25353         if(this.fieldLabel.length){
25354             
25355             content = [
25356                 {
25357                     tag : 'div',
25358                     cls : 'column col-md-12',
25359                     html : this.fieldLabel
25360                 },
25361                 {
25362                     tag : 'div',
25363                     cls : 'column col-md-12',
25364                     cn : managerWidget
25365                 }
25366             ];
25367
25368             if(this.labelAlign == 'left'){
25369                 content = [
25370                     {
25371                         tag : 'div',
25372                         cls : 'column col-md-' + this.labelWidth,
25373                         html : this.fieldLabel
25374                     },
25375                     {
25376                         tag : 'div',
25377                         cls : 'column col-md-' + (12 - this.labelWidth),
25378                         cn : managerWidget
25379                     }
25380                 ];
25381                 
25382             }
25383         }
25384         
25385         var cfg = {
25386             tag : 'div',
25387             cls : 'row clearfix',
25388             cn : content
25389         };
25390         
25391         return cfg;
25392         
25393     },
25394     
25395     initEvents : function()
25396     {
25397         this.managerEl = this.el.select('.roo-document-manager', true).first();
25398         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25399         
25400         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25401         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25402         this.selectorEl.hide();
25403         
25404         if(this.multiple){
25405             this.selectorEl.attr('multiple', 'multiple');
25406         }
25407         
25408         this.selectorEl.on('change', this.onSelect, this);
25409         
25410         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25411         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25412         
25413         this.uploader.on('click', this.onUpload, this);
25414         
25415         var _this = this;
25416         
25417         window.addEventListener("resize", function() { _this.refresh(); } );
25418         
25419         this.fireEvent('initial', this);
25420     },
25421     
25422     onUpload : function(e)
25423     {
25424         e.preventDefault();
25425         
25426         this.selectorEl.dom.click();
25427         
25428     },
25429     
25430     onSelect : function(e)
25431     {
25432         e.preventDefault();
25433         
25434         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25435             return;
25436         }
25437         
25438         Roo.each(this.selectorEl.dom.files, function(file){
25439             if(this.fireEvent('inspect', this, file) != false){
25440                 this.files.push(file);
25441             }
25442         }, this);
25443         
25444         this.process();
25445         
25446     },
25447     
25448     process : function()
25449     {
25450         this.selectorEl.dom.value = '';
25451         
25452         if(!this.files.length){
25453             return;
25454         }
25455         
25456         if(this.files.length > this.boxes){
25457             this.files = this.files.slice(0, this.boxes);
25458         }
25459         
25460         var xhr = new XMLHttpRequest();
25461         
25462         Roo.each(this.files, function(file, index){
25463             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25464                 return;
25465             }
25466             
25467             file.xhr = xhr;
25468             
25469             this.managerEl.createChild({
25470                 tag : 'div',
25471                 cls : 'roo-document-manager-loading',
25472                 cn : [
25473                     {
25474                         tag : 'div',
25475                         tooltip : file.name,
25476                         cls : 'roo-document-manager-thumb',
25477                         html : '<i class="fa fa-spinner fa-pulse"></i>'
25478                     }
25479                 ]
25480
25481             });
25482             
25483         }, this);
25484         
25485         if(this.files.length > this.boxes - 1 ){
25486             this.uploader.hide();
25487         }
25488         
25489         var headers = {
25490             "Accept": "application/json",
25491             "Cache-Control": "no-cache",
25492             "X-Requested-With": "XMLHttpRequest"
25493         };
25494         
25495         xhr.open(this.method, this.url, true);
25496         
25497         for (var headerName in headers) {
25498             var headerValue = headers[headerName];
25499             if (headerValue) {
25500                 xhr.setRequestHeader(headerName, headerValue);
25501             }
25502         }
25503         
25504         var _this = this;
25505         
25506         xhr.onload = function()
25507         {
25508             _this.xhrOnLoad(xhr);
25509         }
25510         
25511         xhr.onerror = function()
25512         {
25513             _this.xhrOnError(xhr);
25514         }
25515         
25516         var formData = new FormData();
25517
25518         formData.append('returnHTML', 'NO');
25519         
25520         Roo.each(this.files, function(file, index){
25521             
25522             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25523                 return;
25524             }
25525             
25526             formData.append(this.getParamName(index), file, file.name);
25527             
25528         }, this);
25529         
25530         if(this.fireEvent('prepare', this, formData) != false){
25531             xhr.send(formData);
25532         };
25533         
25534     },
25535     
25536     getParamName : function(i)
25537     {
25538         if(!this.multiple){
25539             return this.paramName;
25540         }
25541         
25542         return this.paramName + "_" + i;
25543     },
25544     
25545     refresh : function()
25546     {
25547         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25548             el.remove();
25549         }, this);
25550         
25551         
25552         var files = [];
25553         
25554         Roo.each(this.files, function(file){
25555             
25556             if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25557                 return;
25558             }
25559             
25560             if(file.target){
25561                 files.push(file);
25562                 return;
25563             }
25564             
25565             var previewEl = this.managerEl.createChild({
25566                 tag : 'div',
25567                 cls : 'roo-document-manager-preview',
25568                 cn : [
25569                     {
25570                         tag : 'div',
25571                         tooltip : file.filename,
25572                         cls : 'roo-document-manager-thumb',
25573                         html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25574                     },
25575                     {
25576                         tag : 'button',
25577                         cls : 'close',
25578                         html : 'x'
25579                     }
25580                 ]
25581             });
25582             
25583             var close = previewEl.select('button.close', true).first();
25584             
25585             close.on('click', this.onRemove, this, file);
25586             
25587             file.target = previewEl;
25588             
25589             var image = previewEl.select('img', true).first();
25590             
25591             image.on('click', this.onClick, this, file);
25592             
25593             files.push(file);
25594             
25595             return;
25596             
25597         }, this);
25598         
25599         this.files = files;
25600         
25601         this.uploader.show();
25602         
25603         if(this.files.length > this.boxes - 1){
25604             this.uploader.hide();
25605         }
25606         
25607         Roo.isTouch ? this.closable(false) : this.closable(true);
25608         
25609         this.fireEvent('refresh', this);
25610     },
25611     
25612     onRemove : function(e, el, o)
25613     {
25614         e.preventDefault();
25615         
25616         this.fireEvent('remove', this, o);
25617         
25618     },
25619     
25620     remove : function(o)
25621     {
25622         var files = [];
25623         
25624         Roo.each(this.files, function(file){
25625             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25626                 files.push(file);
25627                 return;
25628             }
25629
25630             o.target.remove();
25631
25632         }, this);
25633         
25634         this.files = files;
25635         
25636         this.refresh();
25637     },
25638     
25639     onClick : function(e, el, o)
25640     {
25641         e.preventDefault();
25642         
25643         this.fireEvent('click', this, o);
25644         
25645     },
25646     
25647     closable : function(closable)
25648     {
25649         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25650             
25651             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25652             
25653             if(closable){
25654                 el.show();
25655                 return;
25656             }
25657             
25658             el.hide();
25659             
25660         }, this);
25661     },
25662     
25663     xhrOnLoad : function(xhr)
25664     {
25665         if (xhr.readyState !== 4) {
25666             this.refresh();
25667             this.fireEvent('exception', this, xhr);
25668             return;
25669         }
25670
25671         var response = Roo.decode(xhr.responseText);
25672         
25673         if(!response.success){
25674             this.refresh();
25675             this.fireEvent('exception', this, xhr);
25676             return;
25677         }
25678         
25679         var i = 0;
25680         
25681         Roo.each(this.files, function(file, index){
25682             
25683             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25684                 return;
25685             }
25686             
25687             this.files[index] = response.data[i];
25688             i++;
25689             
25690             return;
25691             
25692         }, this);
25693         
25694         this.refresh();
25695         
25696     },
25697     
25698     xhrOnError : function()
25699     {
25700         Roo.log('xhr on error');
25701         
25702         var response = Roo.decode(xhr.responseText);
25703           
25704         Roo.log(response);
25705     }
25706     
25707     
25708     
25709 });
25710 /*
25711 * Licence: LGPL
25712 */
25713
25714 /**
25715  * @class Roo.bootstrap.DocumentViewer
25716  * @extends Roo.bootstrap.Component
25717  * Bootstrap DocumentViewer class
25718  * 
25719  * @constructor
25720  * Create a new DocumentViewer
25721  * @param {Object} config The config object
25722  */
25723
25724 Roo.bootstrap.DocumentViewer = function(config){
25725     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25726     
25727     this.addEvents({
25728         /**
25729          * @event initial
25730          * Fire after initEvent
25731          * @param {Roo.bootstrap.DocumentViewer} this
25732          */
25733         "initial" : true,
25734         /**
25735          * @event click
25736          * Fire after click
25737          * @param {Roo.bootstrap.DocumentViewer} this
25738          */
25739         "click" : true,
25740         /**
25741          * @event trash
25742          * Fire after trash button
25743          * @param {Roo.bootstrap.DocumentViewer} this
25744          */
25745         "trash" : true
25746         
25747     });
25748 };
25749
25750 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
25751     
25752     getAutoCreate : function()
25753     {
25754         var cfg = {
25755             tag : 'div',
25756             cls : 'roo-document-viewer',
25757             cn : [
25758                 {
25759                     tag : 'div',
25760                     cls : 'roo-document-viewer-body',
25761                     cn : [
25762                         {
25763                             tag : 'div',
25764                             cls : 'roo-document-viewer-thumb',
25765                             cn : [
25766                                 {
25767                                     tag : 'img',
25768                                     cls : 'roo-document-viewer-image'
25769                                 }
25770                             ]
25771                         }
25772                     ]
25773                 },
25774                 {
25775                     tag : 'div',
25776                     cls : 'roo-document-viewer-footer',
25777                     cn : {
25778                         tag : 'div',
25779                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25780                         cn : [
25781                             {
25782                                 tag : 'div',
25783                                 cls : 'btn-group',
25784                                 cn : [
25785                                     {
25786                                         tag : 'button',
25787                                         cls : 'btn btn-default roo-document-viewer-trash',
25788                                         html : '<i class="fa fa-trash"></i>'
25789                                     }
25790                                 ]
25791                             }
25792                         ]
25793                     }
25794                 }
25795             ]
25796         };
25797         
25798         return cfg;
25799     },
25800     
25801     initEvents : function()
25802     {
25803         
25804         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25805         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25806         
25807         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25808         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25809         
25810         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25811         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25812         
25813         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25814         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25815         
25816         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25817         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25818         
25819         this.bodyEl.on('click', this.onClick, this);
25820         
25821         this.trashBtn.on('click', this.onTrash, this);
25822         
25823     },
25824     
25825     initial : function()
25826     {
25827 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25828         
25829         
25830         this.fireEvent('initial', this);
25831         
25832     },
25833     
25834     onClick : function(e)
25835     {
25836         e.preventDefault();
25837         
25838         this.fireEvent('click', this);
25839     },
25840     
25841     onTrash : function(e)
25842     {
25843         e.preventDefault();
25844         
25845         this.fireEvent('trash', this);
25846     }
25847     
25848 });