docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         }
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             }
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 }
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         }
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             }
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         }
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1969
1970         this.el.on("mouseover", this.onMouseOver, this);
1971         this.el.on("mouseout", this.onMouseOut, this);
1972         
1973         
1974     },
1975     findTargetItem : function(e){
1976         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1977         if(!t){
1978             return false;
1979         }
1980         //Roo.log(t);         Roo.log(t.id);
1981         if(t && t.id){
1982             //Roo.log(this.menuitems);
1983             return this.menuitems.get(t.id);
1984             
1985             //return this.items.get(t.menuItemId);
1986         }
1987         
1988         return false;
1989     },
1990     onClick : function(e){
1991         Roo.log("menu.onClick");
1992         var t = this.findTargetItem(e);
1993         if(!t || t.isContainer){
1994             return;
1995         }
1996         Roo.log(e);
1997         /*
1998         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1999             if(t == this.activeItem && t.shouldDeactivate(e)){
2000                 this.activeItem.deactivate();
2001                 delete this.activeItem;
2002                 return;
2003             }
2004             if(t.canActivate){
2005                 this.setActiveItem(t, true);
2006             }
2007             return;
2008             
2009             
2010         }
2011         */
2012        
2013         Roo.log('pass click event');
2014         
2015         t.onClick(e);
2016         
2017         this.fireEvent("click", this, t, e);
2018         
2019         this.hide();
2020     },
2021      onMouseOver : function(e){
2022         var t  = this.findTargetItem(e);
2023         //Roo.log(t);
2024         //if(t){
2025         //    if(t.canActivate && !t.disabled){
2026         //        this.setActiveItem(t, true);
2027         //    }
2028         //}
2029         
2030         this.fireEvent("mouseover", this, e, t);
2031     },
2032     isVisible : function(){
2033         return !this.hidden;
2034     },
2035      onMouseOut : function(e){
2036         var t  = this.findTargetItem(e);
2037         
2038         //if(t ){
2039         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2040         //        this.activeItem.deactivate();
2041         //        delete this.activeItem;
2042         //    }
2043         //}
2044         this.fireEvent("mouseout", this, e, t);
2045     },
2046     
2047     
2048     /**
2049      * Displays this menu relative to another element
2050      * @param {String/HTMLElement/Roo.Element} element The element to align to
2051      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052      * the element (defaults to this.defaultAlign)
2053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2054      */
2055     show : function(el, pos, parentMenu){
2056         this.parentMenu = parentMenu;
2057         if(!this.el){
2058             this.render();
2059         }
2060         this.fireEvent("beforeshow", this);
2061         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2062     },
2063      /**
2064      * Displays this menu at a specific xy position
2065      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     showAt : function(xy, parentMenu, /* private: */_e){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         if(_e !== false){
2074             this.fireEvent("beforeshow", this);
2075             //xy = this.el.adjustForConstraints(xy);
2076         }
2077         
2078         //this.el.show();
2079         this.hideMenuItems();
2080         this.hidden = false;
2081         this.triggerEl.addClass('open');
2082         
2083         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2085         }
2086         
2087         this.el.setXY(xy);
2088         this.focus();
2089         this.fireEvent("show", this);
2090     },
2091     
2092     focus : function(){
2093         return;
2094         if(!this.hidden){
2095             this.doFocus.defer(50, this);
2096         }
2097     },
2098
2099     doFocus : function(){
2100         if(!this.hidden){
2101             this.focusEl.focus();
2102         }
2103     },
2104
2105     /**
2106      * Hides this menu and optionally all parent menus
2107      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2108      */
2109     hide : function(deep){
2110         
2111         this.hideMenuItems();
2112         if(this.el && this.isVisible()){
2113             this.fireEvent("beforehide", this);
2114             if(this.activeItem){
2115                 this.activeItem.deactivate();
2116                 this.activeItem = null;
2117             }
2118             this.triggerEl.removeClass('open');;
2119             this.hidden = true;
2120             this.fireEvent("hide", this);
2121         }
2122         if(deep === true && this.parentMenu){
2123             this.parentMenu.hide(true);
2124         }
2125     },
2126     
2127     onTriggerPress  : function(e)
2128     {
2129         
2130         Roo.log('trigger press');
2131         //Roo.log(e.getTarget());
2132        // Roo.log(this.triggerEl.dom);
2133         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2134             return;
2135         }
2136         
2137         if (this.isVisible()) {
2138             Roo.log('hide');
2139             this.hide();
2140         } else {
2141             Roo.log('show');
2142             this.show(this.triggerEl, false, false);
2143         }
2144         
2145         e.stopEvent();
2146     },
2147     
2148          
2149        
2150     
2151     hideMenuItems : function()
2152     {
2153         //$(backdrop).remove()
2154         Roo.select('.open',true).each(function(aa) {
2155             
2156             aa.removeClass('open');
2157           //var parent = getParent($(this))
2158           //var relatedTarget = { relatedTarget: this }
2159           
2160            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161           //if (e.isDefaultPrevented()) return
2162            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2163         })
2164     },
2165     addxtypeChild : function (tree, cntr) {
2166         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2167           
2168         this.menuitems.add(comp);
2169         return comp;
2170
2171     },
2172     getEl : function()
2173     {
2174         Roo.log(this.el);
2175         return this.el;
2176     }
2177 });
2178
2179  
2180  /*
2181  * - LGPL
2182  *
2183  * menu item
2184  * 
2185  */
2186
2187
2188 /**
2189  * @class Roo.bootstrap.MenuItem
2190  * @extends Roo.bootstrap.Component
2191  * Bootstrap MenuItem class
2192  * @cfg {String} html the menu label
2193  * @cfg {String} href the link
2194  * @cfg {Boolean} preventDefault (true | false) default true
2195  * @cfg {Boolean} isContainer (true | false) default false
2196  * 
2197  * 
2198  * @constructor
2199  * Create a new MenuItem
2200  * @param {Object} config The config object
2201  */
2202
2203
2204 Roo.bootstrap.MenuItem = function(config){
2205     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2206     this.addEvents({
2207         // raw events
2208         /**
2209          * @event click
2210          * The raw click event for the entire grid.
2211          * @param {Roo.bootstrap.MenuItem} this
2212          * @param {Roo.EventObject} e
2213          */
2214         "click" : true
2215     });
2216 };
2217
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2219     
2220     href : false,
2221     html : false,
2222     preventDefault: true,
2223     isContainer : false,
2224     
2225     getAutoCreate : function(){
2226         
2227         if(this.isContainer){
2228             return {
2229                 tag: 'li',
2230                 cls: 'dropdown-menu-item'
2231             };
2232         }
2233         
2234         var cfg= {
2235             tag: 'li',
2236             cls: 'dropdown-menu-item',
2237             cn: [
2238                     {
2239                         tag : 'a',
2240                         href : '#',
2241                         html : 'Link'
2242                     }
2243                 ]
2244         };
2245         if (this.parent().type == 'treeview') {
2246             cfg.cls = 'treeview-menu';
2247         }
2248         
2249         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2251         return cfg;
2252     },
2253     
2254     initEvents: function() {
2255         
2256         //this.el.select('a').on('click', this.onClick, this);
2257         
2258     },
2259     onClick : function(e)
2260     {
2261         Roo.log('item on click ');
2262         //if(this.preventDefault){
2263         //    e.preventDefault();
2264         //}
2265         //this.parent().hideMenuItems();
2266         
2267         this.fireEvent('click', this, e);
2268     },
2269     getEl : function()
2270     {
2271         return this.el;
2272     }
2273 });
2274
2275  
2276
2277  /*
2278  * - LGPL
2279  *
2280  * menu separator
2281  * 
2282  */
2283
2284
2285 /**
2286  * @class Roo.bootstrap.MenuSeparator
2287  * @extends Roo.bootstrap.Component
2288  * Bootstrap MenuSeparator class
2289  * 
2290  * @constructor
2291  * Create a new MenuItem
2292  * @param {Object} config The config object
2293  */
2294
2295
2296 Roo.bootstrap.MenuSeparator = function(config){
2297     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2298 };
2299
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2301     
2302     getAutoCreate : function(){
2303         var cfg = {
2304             cls: 'divider',
2305             tag : 'li'
2306         };
2307         
2308         return cfg;
2309     }
2310    
2311 });
2312
2313  
2314
2315  
2316 /*
2317 * Licence: LGPL
2318 */
2319
2320 /**
2321  * @class Roo.bootstrap.Modal
2322  * @extends Roo.bootstrap.Component
2323  * Bootstrap Modal class
2324  * @cfg {String} title Title of dialog
2325  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2327  * @cfg {Boolean} specificTitle default false
2328  * @cfg {Array} buttons Array of buttons or standard button set..
2329  * @cfg {String} buttonPosition (left|right|center) default right
2330  * @cfg {Boolean} animate default true
2331  * @cfg {Boolean} allow_close default true
2332  * 
2333  * @constructor
2334  * Create a new Modal Dialog
2335  * @param {Object} config The config object
2336  */
2337
2338 Roo.bootstrap.Modal = function(config){
2339     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2340     this.addEvents({
2341         // raw events
2342         /**
2343          * @event btnclick
2344          * The raw btnclick event for the button
2345          * @param {Roo.EventObject} e
2346          */
2347         "btnclick" : true
2348     });
2349     this.buttons = this.buttons || [];
2350      
2351     if (this.tmpl) {
2352         this.tmpl = Roo.factory(this.tmpl);
2353     }
2354     
2355 };
2356
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2358     
2359     title : 'test dialog',
2360    
2361     buttons : false,
2362     
2363     // set on load...
2364      
2365     html: false,
2366     
2367     tmp: false,
2368     
2369     specificTitle: false,
2370     
2371     buttonPosition: 'right',
2372     
2373     allow_close : true,
2374     
2375     animate : true,
2376     
2377     
2378      // private
2379     bodyEl:  false,
2380     footerEl:  false,
2381     titleEl:  false,
2382     closeEl:  false,
2383     
2384     
2385     onRender : function(ct, position)
2386     {
2387         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2388      
2389         if(!this.el){
2390             var cfg = Roo.apply({},  this.getAutoCreate());
2391             cfg.id = Roo.id();
2392             //if(!cfg.name){
2393             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2394             //}
2395             //if (!cfg.name.length) {
2396             //    delete cfg.name;
2397            // }
2398             if (this.cls) {
2399                 cfg.cls += ' ' + this.cls;
2400             }
2401             if (this.style) {
2402                 cfg.style = this.style;
2403             }
2404             this.el = Roo.get(document.body).createChild(cfg, position);
2405         }
2406         //var type = this.el.dom.type;
2407         
2408         
2409         
2410         
2411         if(this.tabIndex !== undefined){
2412             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2413         }
2414         
2415         
2416         this.bodyEl = this.el.select('.modal-body',true).first();
2417         this.closeEl = this.el.select('.modal-header .close', true).first();
2418         this.footerEl = this.el.select('.modal-footer',true).first();
2419         this.titleEl = this.el.select('.modal-title',true).first();
2420         
2421         
2422          
2423         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424         this.maskEl.enableDisplayMode("block");
2425         this.maskEl.hide();
2426         //this.el.addClass("x-dlg-modal");
2427     
2428         if (this.buttons.length) {
2429             Roo.each(this.buttons, function(bb) {
2430                 var b = Roo.apply({}, bb);
2431                 b.xns = b.xns || Roo.bootstrap;
2432                 b.xtype = b.xtype || 'Button';
2433                 if (typeof(b.listeners) == 'undefined') {
2434                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2435                 }
2436                 
2437                 var btn = Roo.factory(b);
2438                 
2439                 btn.onRender(this.el.select('.modal-footer div').first());
2440                 
2441             },this);
2442         }
2443         // render the children.
2444         var nitems = [];
2445         
2446         if(typeof(this.items) != 'undefined'){
2447             var items = this.items;
2448             delete this.items;
2449
2450             for(var i =0;i < items.length;i++) {
2451                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2452             }
2453         }
2454         
2455         this.items = nitems;
2456         
2457         // where are these used - they used to be body/close/footer
2458         
2459        
2460         this.initEvents();
2461         //this.el.addClass([this.fieldClass, this.cls]);
2462         
2463     },
2464     
2465     getAutoCreate : function(){
2466         
2467         
2468         var bdy = {
2469                 cls : 'modal-body',
2470                 html : this.html || ''
2471         };
2472         
2473         var title = {
2474             tag: 'h4',
2475             cls : 'modal-title',
2476             html : this.title
2477         };
2478         
2479         if(this.specificTitle){
2480             title = this.title;
2481             
2482         };
2483         
2484         var header = [];
2485         if (this.allow_close) {
2486             header.push({
2487                 tag: 'button',
2488                 cls : 'close',
2489                 html : '&times'
2490             });
2491         }
2492         header.push(title);
2493         
2494         var modal = {
2495             cls: "modal",
2496             style : 'display: none',
2497             cn : [
2498                 {
2499                     cls: "modal-dialog",
2500                     cn : [
2501                         {
2502                             cls : "modal-content",
2503                             cn : [
2504                                 {
2505                                     cls : 'modal-header',
2506                                     cn : header
2507                                 },
2508                                 bdy,
2509                                 {
2510                                     cls : 'modal-footer',
2511                                     cn : [
2512                                         {
2513                                             tag: 'div',
2514                                             cls: 'btn-' + this.buttonPosition
2515                                         }
2516                                     ]
2517                                     
2518                                 }
2519                                 
2520                                 
2521                             ]
2522                             
2523                         }
2524                     ]
2525                         
2526                 }
2527             ]
2528         };
2529         
2530         if(this.animate){
2531             modal.cls += ' fade';
2532         }
2533         
2534         return modal;
2535           
2536     },
2537     getChildContainer : function() {
2538          
2539          return this.bodyEl;
2540         
2541     },
2542     getButtonContainer : function() {
2543          return this.el.select('.modal-footer div',true).first();
2544         
2545     },
2546     initEvents : function()
2547     {
2548         if (this.allow_close) {
2549             this.closeEl.on('click', this.hide, this);
2550         }
2551         
2552         var _this = this;
2553         
2554         window.addEventListener("resize", function() { _this.resize(); } );
2555
2556     },
2557     
2558     resize : function()
2559     {
2560         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2561     },
2562     
2563     show : function() {
2564         
2565         if (!this.rendered) {
2566             this.render();
2567         }
2568         
2569         this.el.setStyle('display', 'block');
2570         
2571         if(this.animate){
2572             var _this = this;
2573             (function(){ _this.el.addClass('in'); }).defer(50);
2574         }else{
2575             this.el.addClass('in');
2576         }
2577         
2578         // not sure how we can show data in here.. 
2579         //if (this.tmpl) {
2580         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2581         //}
2582         
2583         Roo.get(document.body).addClass("x-body-masked");
2584         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2585         this.maskEl.show();
2586         this.el.setStyle('zIndex', '10001');
2587        
2588         this.fireEvent('show', this);
2589         
2590         
2591     },
2592     hide : function()
2593     {
2594         this.maskEl.hide();
2595         Roo.get(document.body).removeClass("x-body-masked");
2596         this.el.removeClass('in');
2597         
2598         if(this.animate){
2599             var _this = this;
2600             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2601         }else{
2602             this.el.setStyle('display', 'none');
2603         }
2604         
2605         this.fireEvent('hide', this);
2606     },
2607     
2608     addButton : function(str, cb)
2609     {
2610          
2611         
2612         var b = Roo.apply({}, { html : str } );
2613         b.xns = b.xns || Roo.bootstrap;
2614         b.xtype = b.xtype || 'Button';
2615         if (typeof(b.listeners) == 'undefined') {
2616             b.listeners = { click : cb.createDelegate(this)  };
2617         }
2618         
2619         var btn = Roo.factory(b);
2620            
2621         btn.onRender(this.el.select('.modal-footer div').first());
2622         
2623         return btn;   
2624        
2625     },
2626     
2627     setDefaultButton : function(btn)
2628     {
2629         //this.el.select('.modal-footer').()
2630     },
2631     resizeTo: function(w,h)
2632     {
2633         // skip..
2634     },
2635     setContentSize  : function(w, h)
2636     {
2637         
2638     },
2639     onButtonClick: function(btn,e)
2640     {
2641         //Roo.log([a,b,c]);
2642         this.fireEvent('btnclick', btn.name, e);
2643     },
2644      /**
2645      * Set the title of the Dialog
2646      * @param {String} str new Title
2647      */
2648     setTitle: function(str) {
2649         this.titleEl.dom.innerHTML = str;    
2650     },
2651     /**
2652      * Set the body of the Dialog
2653      * @param {String} str new Title
2654      */
2655     setBody: function(str) {
2656         this.bodyEl.dom.innerHTML = str;    
2657     },
2658     /**
2659      * Set the body of the Dialog using the template
2660      * @param {Obj} data - apply this data to the template and replace the body contents.
2661      */
2662     applyBody: function(obj)
2663     {
2664         if (!this.tmpl) {
2665             Roo.log("Error - using apply Body without a template");
2666             //code
2667         }
2668         this.tmpl.overwrite(this.bodyEl, obj);
2669     }
2670     
2671 });
2672
2673
2674 Roo.apply(Roo.bootstrap.Modal,  {
2675     /**
2676          * Button config that displays a single OK button
2677          * @type Object
2678          */
2679         OK :  [{
2680             name : 'ok',
2681             weight : 'primary',
2682             html : 'OK'
2683         }], 
2684         /**
2685          * Button config that displays Yes and No buttons
2686          * @type Object
2687          */
2688         YESNO : [
2689             {
2690                 name  : 'no',
2691                 html : 'No'
2692             },
2693             {
2694                 name  :'yes',
2695                 weight : 'primary',
2696                 html : 'Yes'
2697             }
2698         ],
2699         
2700         /**
2701          * Button config that displays OK and Cancel buttons
2702          * @type Object
2703          */
2704         OKCANCEL : [
2705             {
2706                name : 'cancel',
2707                 html : 'Cancel'
2708             },
2709             {
2710                 name : 'ok',
2711                 weight : 'primary',
2712                 html : 'OK'
2713             }
2714         ],
2715         /**
2716          * Button config that displays Yes, No and Cancel buttons
2717          * @type Object
2718          */
2719         YESNOCANCEL : [
2720             {
2721                 name : 'yes',
2722                 weight : 'primary',
2723                 html : 'Yes'
2724             },
2725             {
2726                 name : 'no',
2727                 html : 'No'
2728             },
2729             {
2730                 name : 'cancel',
2731                 html : 'Cancel'
2732             }
2733         ]
2734 });
2735  
2736  /*
2737  * - LGPL
2738  *
2739  * messagebox - can be used as a replace
2740  * 
2741  */
2742 /**
2743  * @class Roo.MessageBox
2744  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2745  * Example usage:
2746  *<pre><code>
2747 // Basic alert:
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2749
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2752     if (btn == 'ok'){
2753         // process text value...
2754     }
2755 });
2756
2757 // Show a dialog using config options:
2758 Roo.Msg.show({
2759    title:'Save Changes?',
2760    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761    buttons: Roo.Msg.YESNOCANCEL,
2762    fn: processResult,
2763    animEl: 'elId'
2764 });
2765 </code></pre>
2766  * @singleton
2767  */
2768 Roo.bootstrap.MessageBox = function(){
2769     var dlg, opt, mask, waitTimer;
2770     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771     var buttons, activeTextEl, bwidth;
2772
2773     
2774     // private
2775     var handleButton = function(button){
2776         dlg.hide();
2777         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2778     };
2779
2780     // private
2781     var handleHide = function(){
2782         if(opt && opt.cls){
2783             dlg.el.removeClass(opt.cls);
2784         }
2785         //if(waitTimer){
2786         //    Roo.TaskMgr.stop(waitTimer);
2787         //    waitTimer = null;
2788         //}
2789     };
2790
2791     // private
2792     var updateButtons = function(b){
2793         var width = 0;
2794         if(!b){
2795             buttons["ok"].hide();
2796             buttons["cancel"].hide();
2797             buttons["yes"].hide();
2798             buttons["no"].hide();
2799             //dlg.footer.dom.style.display = 'none';
2800             return width;
2801         }
2802         dlg.footerEl.dom.style.display = '';
2803         for(var k in buttons){
2804             if(typeof buttons[k] != "function"){
2805                 if(b[k]){
2806                     buttons[k].show();
2807                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808                     width += buttons[k].el.getWidth()+15;
2809                 }else{
2810                     buttons[k].hide();
2811                 }
2812             }
2813         }
2814         return width;
2815     };
2816
2817     // private
2818     var handleEsc = function(d, k, e){
2819         if(opt && opt.closable !== false){
2820             dlg.hide();
2821         }
2822         if(e){
2823             e.stopEvent();
2824         }
2825     };
2826
2827     return {
2828         /**
2829          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830          * @return {Roo.BasicDialog} The BasicDialog element
2831          */
2832         getDialog : function(){
2833            if(!dlg){
2834                 dlg = new Roo.bootstrap.Modal( {
2835                     //draggable: true,
2836                     //resizable:false,
2837                     //constraintoviewport:false,
2838                     //fixedcenter:true,
2839                     //collapsible : false,
2840                     //shim:true,
2841                     //modal: true,
2842                   //  width:400,
2843                   //  height:100,
2844                     //buttonAlign:"center",
2845                     closeClick : function(){
2846                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2847                             handleButton("no");
2848                         }else{
2849                             handleButton("cancel");
2850                         }
2851                     }
2852                 });
2853                 dlg.render();
2854                 dlg.on("hide", handleHide);
2855                 mask = dlg.mask;
2856                 //dlg.addKeyListener(27, handleEsc);
2857                 buttons = {};
2858                 this.buttons = buttons;
2859                 var bt = this.buttonText;
2860                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2864                 Roo.log(buttons)
2865                 bodyEl = dlg.bodyEl.createChild({
2866
2867                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868                         '<textarea class="roo-mb-textarea"></textarea>' +
2869                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2870                 });
2871                 msgEl = bodyEl.dom.firstChild;
2872                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873                 textboxEl.enableDisplayMode();
2874                 textboxEl.addKeyListener([10,13], function(){
2875                     if(dlg.isVisible() && opt && opt.buttons){
2876                         if(opt.buttons.ok){
2877                             handleButton("ok");
2878                         }else if(opt.buttons.yes){
2879                             handleButton("yes");
2880                         }
2881                     }
2882                 });
2883                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884                 textareaEl.enableDisplayMode();
2885                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886                 progressEl.enableDisplayMode();
2887                 var pf = progressEl.dom.firstChild;
2888                 if (pf) {
2889                     pp = Roo.get(pf.firstChild);
2890                     pp.setHeight(pf.offsetHeight);
2891                 }
2892                 
2893             }
2894             return dlg;
2895         },
2896
2897         /**
2898          * Updates the message box body text
2899          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900          * the XHTML-compliant non-breaking space character '&amp;#160;')
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         updateText : function(text){
2904             if(!dlg.isVisible() && !opt.width){
2905                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2906             }
2907             msgEl.innerHTML = text || '&#160;';
2908       
2909             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2911             var w = Math.max(
2912                     Math.min(opt.width || cw , this.maxWidth), 
2913                     Math.max(opt.minWidth || this.minWidth, bwidth)
2914             );
2915             if(opt.prompt){
2916                 activeTextEl.setWidth(w);
2917             }
2918             if(dlg.isVisible()){
2919                 dlg.fixedcenter = false;
2920             }
2921             // to big, make it scroll. = But as usual stupid IE does not support
2922             // !important..
2923             
2924             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2927             } else {
2928                 bodyEl.dom.style.height = '';
2929                 bodyEl.dom.style.overflowY = '';
2930             }
2931             if (cw > w) {
2932                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2933             } else {
2934                 bodyEl.dom.style.overflowX = '';
2935             }
2936             
2937             dlg.setContentSize(w, bodyEl.getHeight());
2938             if(dlg.isVisible()){
2939                 dlg.fixedcenter = true;
2940             }
2941             return this;
2942         },
2943
2944         /**
2945          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2946          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949          * @return {Roo.MessageBox} This message box
2950          */
2951         updateProgress : function(value, text){
2952             if(text){
2953                 this.updateText(text);
2954             }
2955             if (pp) { // weird bug on my firefox - for some reason this is not defined
2956                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2957             }
2958             return this;
2959         },        
2960
2961         /**
2962          * Returns true if the message box is currently displayed
2963          * @return {Boolean} True if the message box is visible, else false
2964          */
2965         isVisible : function(){
2966             return dlg && dlg.isVisible();  
2967         },
2968
2969         /**
2970          * Hides the message box if it is displayed
2971          */
2972         hide : function(){
2973             if(this.isVisible()){
2974                 dlg.hide();
2975             }  
2976         },
2977
2978         /**
2979          * Displays a new message box, or reinitializes an existing message box, based on the config options
2980          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981          * The following config object properties are supported:
2982          * <pre>
2983 Property    Type             Description
2984 ----------  ---------------  ------------------------------------------------------------------------------------
2985 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2986                                    closes (defaults to undefined)
2987 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2990                                    progress and wait dialogs will ignore this property and always hide the
2991                                    close button as they can only be closed programmatically.
2992 cls               String           A custom CSS class to apply to the message box element
2993 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2994                                    displayed (defaults to 75)
2995 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2996                                    function will be btn (the name of the button that was clicked, if applicable,
2997                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2998                                    Progress and wait dialogs will ignore this option since they do not respond to
2999                                    user actions and can only be closed programmatically, so any required function
3000                                    should be called by the same code after it closes the dialog.
3001 icon              String           A CSS class that provides a background image to be used as an icon for
3002                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3004 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3005 modal             Boolean          False to allow user interaction with the page while the message box is
3006                                    displayed (defaults to true)
3007 msg               String           A string that will replace the existing message box body text (defaults
3008                                    to the XHTML-compliant non-breaking space character '&#160;')
3009 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3010 progress          Boolean          True to display a progress bar (defaults to false)
3011 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3014 title             String           The title text
3015 value             String           The string value to set into the active textbox element if displayed
3016 wait              Boolean          True to display a progress bar (defaults to false)
3017 width             Number           The width of the dialog in pixels
3018 </pre>
3019          *
3020          * Example usage:
3021          * <pre><code>
3022 Roo.Msg.show({
3023    title: 'Address',
3024    msg: 'Please enter your address:',
3025    width: 300,
3026    buttons: Roo.MessageBox.OKCANCEL,
3027    multiline: true,
3028    fn: saveAddress,
3029    animEl: 'addAddressBtn'
3030 });
3031 </code></pre>
3032          * @param {Object} config Configuration options
3033          * @return {Roo.MessageBox} This message box
3034          */
3035         show : function(options)
3036         {
3037             
3038             // this causes nightmares if you show one dialog after another
3039             // especially on callbacks..
3040              
3041             if(this.isVisible()){
3042                 
3043                 this.hide();
3044                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3046                 Roo.log("New Dialog Message:" +  options.msg )
3047                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3049                 
3050             }
3051             var d = this.getDialog();
3052             opt = options;
3053             d.setTitle(opt.title || "&#160;");
3054             d.closeEl.setDisplayed(opt.closable !== false);
3055             activeTextEl = textboxEl;
3056             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3057             if(opt.prompt){
3058                 if(opt.multiline){
3059                     textboxEl.hide();
3060                     textareaEl.show();
3061                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3062                         opt.multiline : this.defaultTextHeight);
3063                     activeTextEl = textareaEl;
3064                 }else{
3065                     textboxEl.show();
3066                     textareaEl.hide();
3067                 }
3068             }else{
3069                 textboxEl.hide();
3070                 textareaEl.hide();
3071             }
3072             progressEl.setDisplayed(opt.progress === true);
3073             this.updateProgress(0);
3074             activeTextEl.dom.value = opt.value || "";
3075             if(opt.prompt){
3076                 dlg.setDefaultButton(activeTextEl);
3077             }else{
3078                 var bs = opt.buttons;
3079                 var db = null;
3080                 if(bs && bs.ok){
3081                     db = buttons["ok"];
3082                 }else if(bs && bs.yes){
3083                     db = buttons["yes"];
3084                 }
3085                 dlg.setDefaultButton(db);
3086             }
3087             bwidth = updateButtons(opt.buttons);
3088             this.updateText(opt.msg);
3089             if(opt.cls){
3090                 d.el.addClass(opt.cls);
3091             }
3092             d.proxyDrag = opt.proxyDrag === true;
3093             d.modal = opt.modal !== false;
3094             d.mask = opt.modal !== false ? mask : false;
3095             if(!d.isVisible()){
3096                 // force it to the end of the z-index stack so it gets a cursor in FF
3097                 document.body.appendChild(dlg.el.dom);
3098                 d.animateTarget = null;
3099                 d.show(options.animEl);
3100             }
3101             return this;
3102         },
3103
3104         /**
3105          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3106          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107          * and closing the message box when the process is complete.
3108          * @param {String} title The title bar text
3109          * @param {String} msg The message box body text
3110          * @return {Roo.MessageBox} This message box
3111          */
3112         progress : function(title, msg){
3113             this.show({
3114                 title : title,
3115                 msg : msg,
3116                 buttons: false,
3117                 progress:true,
3118                 closable:false,
3119                 minWidth: this.minProgressWidth,
3120                 modal : true
3121             });
3122             return this;
3123         },
3124
3125         /**
3126          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127          * If a callback function is passed it will be called after the user clicks the button, and the
3128          * id of the button that was clicked will be passed as the only parameter to the callback
3129          * (could also be the top-right close button).
3130          * @param {String} title The title bar text
3131          * @param {String} msg The message box body text
3132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133          * @param {Object} scope (optional) The scope of the callback function
3134          * @return {Roo.MessageBox} This message box
3135          */
3136         alert : function(title, msg, fn, scope){
3137             this.show({
3138                 title : title,
3139                 msg : msg,
3140                 buttons: this.OK,
3141                 fn: fn,
3142                 scope : scope,
3143                 modal : true
3144             });
3145             return this;
3146         },
3147
3148         /**
3149          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3150          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151          * You are responsible for closing the message box when the process is complete.
3152          * @param {String} msg The message box body text
3153          * @param {String} title (optional) The title bar text
3154          * @return {Roo.MessageBox} This message box
3155          */
3156         wait : function(msg, title){
3157             this.show({
3158                 title : title,
3159                 msg : msg,
3160                 buttons: false,
3161                 closable:false,
3162                 progress:true,
3163                 modal:true,
3164                 width:300,
3165                 wait:true
3166             });
3167             waitTimer = Roo.TaskMgr.start({
3168                 run: function(i){
3169                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3170                 },
3171                 interval: 1000
3172             });
3173             return this;
3174         },
3175
3176         /**
3177          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180          * @param {String} title The title bar text
3181          * @param {String} msg The message box body text
3182          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183          * @param {Object} scope (optional) The scope of the callback function
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         confirm : function(title, msg, fn, scope){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: this.YESNO,
3191                 fn: fn,
3192                 scope : scope,
3193                 modal : true
3194             });
3195             return this;
3196         },
3197
3198         /**
3199          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3201          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202          * (could also be the top-right close button) and the text that was entered will be passed as the two
3203          * parameters to the callback.
3204          * @param {String} title The title bar text
3205          * @param {String} msg The message box body text
3206          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207          * @param {Object} scope (optional) The scope of the callback function
3208          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210          * @return {Roo.MessageBox} This message box
3211          */
3212         prompt : function(title, msg, fn, scope, multiline){
3213             this.show({
3214                 title : title,
3215                 msg : msg,
3216                 buttons: this.OKCANCEL,
3217                 fn: fn,
3218                 minWidth:250,
3219                 scope : scope,
3220                 prompt:true,
3221                 multiline: multiline,
3222                 modal : true
3223             });
3224             return this;
3225         },
3226
3227         /**
3228          * Button config that displays a single OK button
3229          * @type Object
3230          */
3231         OK : {ok:true},
3232         /**
3233          * Button config that displays Yes and No buttons
3234          * @type Object
3235          */
3236         YESNO : {yes:true, no:true},
3237         /**
3238          * Button config that displays OK and Cancel buttons
3239          * @type Object
3240          */
3241         OKCANCEL : {ok:true, cancel:true},
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : {yes:true, no:true, cancel:true},
3247
3248         /**
3249          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3250          * @type Number
3251          */
3252         defaultTextHeight : 75,
3253         /**
3254          * The maximum width in pixels of the message box (defaults to 600)
3255          * @type Number
3256          */
3257         maxWidth : 600,
3258         /**
3259          * The minimum width in pixels of the message box (defaults to 100)
3260          * @type Number
3261          */
3262         minWidth : 100,
3263         /**
3264          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3265          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3266          * @type Number
3267          */
3268         minProgressWidth : 250,
3269         /**
3270          * An object containing the default button text strings that can be overriden for localized language support.
3271          * Supported properties are: ok, cancel, yes and no.
3272          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3273          * @type Object
3274          */
3275         buttonText : {
3276             ok : "OK",
3277             cancel : "Cancel",
3278             yes : "Yes",
3279             no : "No"
3280         }
3281     };
3282 }();
3283
3284 /**
3285  * Shorthand for {@link Roo.MessageBox}
3286  */
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3289 /*
3290  * - LGPL
3291  *
3292  * navbar
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.Navbar
3298  * @extends Roo.bootstrap.Component
3299  * Bootstrap Navbar class
3300
3301  * @constructor
3302  * Create a new Navbar
3303  * @param {Object} config The config object
3304  */
3305
3306
3307 Roo.bootstrap.Navbar = function(config){
3308     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3309     
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3313     
3314     
3315    
3316     // private
3317     navItems : false,
3318     loadMask : false,
3319     
3320     
3321     getAutoCreate : function(){
3322         
3323         
3324         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3325         
3326     },
3327     
3328     initEvents :function ()
3329     {
3330         //Roo.log(this.el.select('.navbar-toggle',true));
3331         this.el.select('.navbar-toggle',true).on('click', function() {
3332            // Roo.log('click');
3333             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3334         }, this);
3335         
3336         var mark = {
3337             tag: "div",
3338             cls:"x-dlg-mask"
3339         }
3340         
3341         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3342         
3343         var size = this.el.getSize();
3344         this.maskEl.setSize(size.width, size.height);
3345         this.maskEl.enableDisplayMode("block");
3346         this.maskEl.hide();
3347         
3348         if(this.loadMask){
3349             this.maskEl.show();
3350         }
3351     },
3352     
3353     
3354     getChildContainer : function()
3355     {
3356         if (this.el.select('.collapse').getCount()) {
3357             return this.el.select('.collapse',true).first();
3358         }
3359         
3360         return this.el;
3361     },
3362     
3363     mask : function()
3364     {
3365         this.maskEl.show();
3366     },
3367     
3368     unmask : function()
3369     {
3370         this.maskEl.hide();
3371     } 
3372     
3373     
3374     
3375     
3376 });
3377
3378
3379
3380  
3381
3382  /*
3383  * - LGPL
3384  *
3385  * navbar
3386  * 
3387  */
3388
3389 /**
3390  * @class Roo.bootstrap.NavSimplebar
3391  * @extends Roo.bootstrap.Navbar
3392  * Bootstrap Sidebar class
3393  *
3394  * @cfg {Boolean} inverse is inverted color
3395  * 
3396  * @cfg {String} type (nav | pills | tabs)
3397  * @cfg {Boolean} arrangement stacked | justified
3398  * @cfg {String} align (left | right) alignment
3399  * 
3400  * @cfg {Boolean} main (true|false) main nav bar? default false
3401  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3402  * 
3403  * @cfg {String} tag (header|footer|nav|div) default is nav 
3404
3405  * 
3406  * 
3407  * 
3408  * @constructor
3409  * Create a new Sidebar
3410  * @param {Object} config The config object
3411  */
3412
3413
3414 Roo.bootstrap.NavSimplebar = function(config){
3415     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3416 };
3417
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3419     
3420     inverse: false,
3421     
3422     type: false,
3423     arrangement: '',
3424     align : false,
3425     
3426     
3427     
3428     main : false,
3429     
3430     
3431     tag : false,
3432     
3433     
3434     getAutoCreate : function(){
3435         
3436         
3437         var cfg = {
3438             tag : this.tag || 'div',
3439             cls : 'navbar'
3440         };
3441           
3442         
3443         cfg.cn = [
3444             {
3445                 cls: 'nav',
3446                 tag : 'ul'
3447             }
3448         ];
3449         
3450          
3451         this.type = this.type || 'nav';
3452         if (['tabs','pills'].indexOf(this.type)!==-1) {
3453             cfg.cn[0].cls += ' nav-' + this.type
3454         
3455         
3456         } else {
3457             if (this.type!=='nav') {
3458                 Roo.log('nav type must be nav/tabs/pills')
3459             }
3460             cfg.cn[0].cls += ' navbar-nav'
3461         }
3462         
3463         
3464         
3465         
3466         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467             cfg.cn[0].cls += ' nav-' + this.arrangement;
3468         }
3469         
3470         
3471         if (this.align === 'right') {
3472             cfg.cn[0].cls += ' navbar-right';
3473         }
3474         
3475         if (this.inverse) {
3476             cfg.cls += ' navbar-inverse';
3477             
3478         }
3479         
3480         
3481         return cfg;
3482     
3483         
3484     }
3485     
3486     
3487     
3488 });
3489
3490
3491
3492  
3493
3494  
3495        /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.NavHeaderbar
3504  * @extends Roo.bootstrap.NavSimplebar
3505  * Bootstrap Sidebar class
3506  *
3507  * @cfg {String} brand what is brand
3508  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509  * @cfg {String} brand_href href of the brand
3510  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3511  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3514  * 
3515  * @constructor
3516  * Create a new Sidebar
3517  * @param {Object} config The config object
3518  */
3519
3520
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3523       
3524 };
3525
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3527     
3528     position: '',
3529     brand: '',
3530     brand_href: false,
3531     srButton : true,
3532     autohide : false,
3533     desktopCenter : false,
3534    
3535     
3536     getAutoCreate : function(){
3537         
3538         var   cfg = {
3539             tag: this.nav || 'nav',
3540             cls: 'navbar',
3541             role: 'navigation',
3542             cn: []
3543         };
3544         
3545         var cn = cfg.cn;
3546         if (this.desktopCenter) {
3547             cn.push({cls : 'container', cn : []});
3548             cn = cn[0].cn;
3549         }
3550         
3551         if(this.srButton){
3552             cn.push({
3553                 tag: 'div',
3554                 cls: 'navbar-header',
3555                 cn: [
3556                     {
3557                         tag: 'button',
3558                         type: 'button',
3559                         cls: 'navbar-toggle',
3560                         'data-toggle': 'collapse',
3561                         cn: [
3562                             {
3563                                 tag: 'span',
3564                                 cls: 'sr-only',
3565                                 html: 'Toggle navigation'
3566                             },
3567                             {
3568                                 tag: 'span',
3569                                 cls: 'icon-bar'
3570                             },
3571                             {
3572                                 tag: 'span',
3573                                 cls: 'icon-bar'
3574                             },
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'icon-bar'
3578                             }
3579                         ]
3580                     }
3581                 ]
3582             });
3583         }
3584         
3585         cn.push({
3586             tag: 'div',
3587             cls: 'collapse navbar-collapse',
3588             cn : []
3589         });
3590         
3591         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3592         
3593         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594             cfg.cls += ' navbar-' + this.position;
3595             
3596             // tag can override this..
3597             
3598             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3599         }
3600         
3601         if (this.brand !== '') {
3602             cn[0].cn.push({
3603                 tag: 'a',
3604                 href: this.brand_href ? this.brand_href : '#',
3605                 cls: 'navbar-brand',
3606                 cn: [
3607                 this.brand
3608                 ]
3609             });
3610         }
3611         
3612         if(this.main){
3613             cfg.cls += ' main-nav';
3614         }
3615         
3616         
3617         return cfg;
3618
3619         
3620     },
3621     getHeaderChildContainer : function()
3622     {
3623         if (this.el.select('.navbar-header').getCount()) {
3624             return this.el.select('.navbar-header',true).first();
3625         }
3626         
3627         return this.getChildContainer();
3628     },
3629     
3630     
3631     initEvents : function()
3632     {
3633         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3634         
3635         if (this.autohide) {
3636             
3637             var prevScroll = 0;
3638             var ft = this.el;
3639             
3640             Roo.get(document).on('scroll',function(e) {
3641                 var ns = Roo.get(document).getScroll().top;
3642                 var os = prevScroll;
3643                 prevScroll = ns;
3644                 
3645                 if(ns > os){
3646                     ft.removeClass('slideDown');
3647                     ft.addClass('slideUp');
3648                     return;
3649                 }
3650                 ft.removeClass('slideUp');
3651                 ft.addClass('slideDown');
3652                  
3653               
3654           },this);
3655         }
3656     }    
3657     
3658 });
3659
3660
3661
3662  
3663
3664  /*
3665  * - LGPL
3666  *
3667  * navbar
3668  * 
3669  */
3670
3671 /**
3672  * @class Roo.bootstrap.NavSidebar
3673  * @extends Roo.bootstrap.Navbar
3674  * Bootstrap Sidebar class
3675  * 
3676  * @constructor
3677  * Create a new Sidebar
3678  * @param {Object} config The config object
3679  */
3680
3681
3682 Roo.bootstrap.NavSidebar = function(config){
3683     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3684 };
3685
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3687     
3688     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3689     
3690     getAutoCreate : function(){
3691         
3692         
3693         return  {
3694             tag: 'div',
3695             cls: 'sidebar sidebar-nav'
3696         };
3697     
3698         
3699     }
3700     
3701     
3702     
3703 });
3704
3705
3706
3707  
3708
3709  /*
3710  * - LGPL
3711  *
3712  * nav group
3713  * 
3714  */
3715
3716 /**
3717  * @class Roo.bootstrap.NavGroup
3718  * @extends Roo.bootstrap.Component
3719  * Bootstrap NavGroup class
3720  * @cfg {String} align (left|right)
3721  * @cfg {Boolean} inverse
3722  * @cfg {String} type (nav|pills|tab) default nav
3723  * @cfg {String} navId - reference Id for navbar.
3724
3725  * 
3726  * @constructor
3727  * Create a new nav group
3728  * @param {Object} config The config object
3729  */
3730
3731 Roo.bootstrap.NavGroup = function(config){
3732     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3733     this.navItems = [];
3734    
3735     Roo.bootstrap.NavGroup.register(this);
3736      this.addEvents({
3737         /**
3738              * @event changed
3739              * Fires when the active item changes
3740              * @param {Roo.bootstrap.NavGroup} this
3741              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3743          */
3744         'changed': true
3745      });
3746     
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3750     
3751     align: '',
3752     inverse: false,
3753     form: false,
3754     type: 'nav',
3755     navId : '',
3756     // private
3757     
3758     navItems : false, 
3759     
3760     getAutoCreate : function()
3761     {
3762         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3763         
3764         cfg = {
3765             tag : 'ul',
3766             cls: 'nav' 
3767         }
3768         
3769         if (['tabs','pills'].indexOf(this.type)!==-1) {
3770             cfg.cls += ' nav-' + this.type
3771         } else {
3772             if (this.type!=='nav') {
3773                 Roo.log('nav type must be nav/tabs/pills')
3774             }
3775             cfg.cls += ' navbar-nav'
3776         }
3777         
3778         if (this.parent().sidebar) {
3779             cfg = {
3780                 tag: 'ul',
3781                 cls: 'dashboard-menu sidebar-menu'
3782             }
3783             
3784             return cfg;
3785         }
3786         
3787         if (this.form === true) {
3788             cfg = {
3789                 tag: 'form',
3790                 cls: 'navbar-form'
3791             }
3792             
3793             if (this.align === 'right') {
3794                 cfg.cls += ' navbar-right';
3795             } else {
3796                 cfg.cls += ' navbar-left';
3797             }
3798         }
3799         
3800         if (this.align === 'right') {
3801             cfg.cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     },
3812     /**
3813     * sets the active Navigation item
3814     * @param {Roo.bootstrap.NavItem} the new current navitem
3815     */
3816     setActiveItem : function(item)
3817     {
3818         var prev = false;
3819         Roo.each(this.navItems, function(v){
3820             if (v == item) {
3821                 return ;
3822             }
3823             if (v.isActive()) {
3824                 v.setActive(false, true);
3825                 prev = v;
3826                 
3827             }
3828             
3829         });
3830
3831         item.setActive(true, true);
3832         this.fireEvent('changed', this, item, prev);
3833         
3834         
3835     },
3836     /**
3837     * gets the active Navigation item
3838     * @return {Roo.bootstrap.NavItem} the current navitem
3839     */
3840     getActive : function()
3841     {
3842         
3843         var prev = false;
3844         Roo.each(this.navItems, function(v){
3845             
3846             if (v.isActive()) {
3847                 prev = v;
3848                 
3849             }
3850             
3851         });
3852         return prev;
3853     },
3854     
3855     indexOfNav : function()
3856     {
3857         
3858         var prev = false;
3859         Roo.each(this.navItems, function(v,i){
3860             
3861             if (v.isActive()) {
3862                 prev = i;
3863                 
3864             }
3865             
3866         });
3867         return prev;
3868     },
3869     /**
3870     * adds a Navigation item
3871     * @param {Roo.bootstrap.NavItem} the navitem to add
3872     */
3873     addItem : function(cfg)
3874     {
3875         var cn = new Roo.bootstrap.NavItem(cfg);
3876         this.register(cn);
3877         cn.parentId = this.id;
3878         cn.onRender(this.el, null);
3879         return cn;
3880     },
3881     /**
3882     * register a Navigation item
3883     * @param {Roo.bootstrap.NavItem} the navitem to add
3884     */
3885     register : function(item)
3886     {
3887         this.navItems.push( item);
3888         item.navId = this.navId;
3889     
3890     },
3891     
3892     /**
3893     * clear all the Navigation item
3894     */
3895    
3896     clearAll : function()
3897     {
3898         this.navItems = [];
3899         this.el.dom.innerHTML = '';
3900     },
3901     
3902     getNavItem: function(tabId)
3903     {
3904         var ret = false;
3905         Roo.each(this.navItems, function(e) {
3906             if (e.tabId == tabId) {
3907                ret =  e;
3908                return false;
3909             }
3910             return true;
3911             
3912         });
3913         return ret;
3914     },
3915     
3916     setActiveNext : function()
3917     {
3918         var i = this.indexOfNav(this.getActive());
3919         if (i > this.navItems.length) {
3920             return;
3921         }
3922         this.setActiveItem(this.navItems[i+1]);
3923     },
3924     setActivePrev : function()
3925     {
3926         var i = this.indexOfNav(this.getActive());
3927         if (i  < 1) {
3928             return;
3929         }
3930         this.setActiveItem(this.navItems[i-1]);
3931     },
3932     clearWasActive : function(except) {
3933         Roo.each(this.navItems, function(e) {
3934             if (e.tabId != except.tabId && e.was_active) {
3935                e.was_active = false;
3936                return false;
3937             }
3938             return true;
3939             
3940         });
3941     },
3942     getWasActive : function ()
3943     {
3944         var r = false;
3945         Roo.each(this.navItems, function(e) {
3946             if (e.was_active) {
3947                r = e;
3948                return false;
3949             }
3950             return true;
3951             
3952         });
3953         return r;
3954     }
3955     
3956     
3957 });
3958
3959  
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3961     
3962     groups: {},
3963      /**
3964     * register a Navigation Group
3965     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3966     */
3967     register : function(navgrp)
3968     {
3969         this.groups[navgrp.navId] = navgrp;
3970         
3971     },
3972     /**
3973     * fetch a Navigation Group based on the navigation ID
3974     * @param {string} the navgroup to add
3975     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3976     */
3977     get: function(navId) {
3978         if (typeof(this.groups[navId]) == 'undefined') {
3979             return false;
3980             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3981         }
3982         return this.groups[navId] ;
3983     }
3984     
3985     
3986     
3987 });
3988
3989  /*
3990  * - LGPL
3991  *
3992  * row
3993  * 
3994  */
3995
3996 /**
3997  * @class Roo.bootstrap.NavItem
3998  * @extends Roo.bootstrap.Component
3999  * Bootstrap Navbar.NavItem class
4000  * @cfg {String} href  link to
4001  * @cfg {String} html content of button
4002  * @cfg {String} badge text inside badge
4003  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004  * @cfg {String} glyphicon name of glyphicon
4005  * @cfg {String} icon name of font awesome icon
4006  * @cfg {Boolean} active Is item active
4007  * @cfg {Boolean} disabled Is item disabled
4008  
4009  * @cfg {Boolean} preventDefault (true | false) default false
4010  * @cfg {String} tabId the tab that this item activates.
4011  * @cfg {String} tagtype (a|span) render as a href or span?
4012  * @cfg {Boolean} animateRef (true|false) link to element default false  
4013   
4014  * @constructor
4015  * Create a new Navbar Item
4016  * @param {Object} config The config object
4017  */
4018 Roo.bootstrap.NavItem = function(config){
4019     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4020     this.addEvents({
4021         // raw events
4022         /**
4023          * @event click
4024          * The raw click event for the entire grid.
4025          * @param {Roo.EventObject} e
4026          */
4027         "click" : true,
4028          /**
4029             * @event changed
4030             * Fires when the active item active state changes
4031             * @param {Roo.bootstrap.NavItem} this
4032             * @param {boolean} state the new state
4033              
4034          */
4035         'changed': true,
4036         /**
4037             * @event scrollto
4038             * Fires when scroll to element
4039             * @param {Roo.bootstrap.NavItem} this
4040             * @param {Object} options
4041             * @param {Roo.EventObject} e
4042              
4043          */
4044         'scrollto': true
4045     });
4046    
4047 };
4048
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4050     
4051     href: false,
4052     html: '',
4053     badge: '',
4054     icon: false,
4055     glyphicon: false,
4056     active: false,
4057     preventDefault : false,
4058     tabId : false,
4059     tagtype : 'a',
4060     disabled : false,
4061     animateRef : false,
4062     was_active : false,
4063     
4064     getAutoCreate : function(){
4065          
4066         var cfg = {
4067             tag: 'li',
4068             cls: 'nav-item'
4069             
4070         }
4071         if (this.active) {
4072             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4073         }
4074         if (this.disabled) {
4075             cfg.cls += ' disabled';
4076         }
4077         
4078         if (this.href || this.html || this.glyphicon || this.icon) {
4079             cfg.cn = [
4080                 {
4081                     tag: this.tagtype,
4082                     href : this.href || "#",
4083                     html: this.html || ''
4084                 }
4085             ];
4086             
4087             if (this.icon) {
4088                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4089             }
4090
4091             if(this.glyphicon) {
4092                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4093             }
4094             
4095             if (this.menu) {
4096                 
4097                 cfg.cn[0].html += " <span class='caret'></span>";
4098              
4099             }
4100             
4101             if (this.badge !== '') {
4102                  
4103                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4104             }
4105         }
4106         
4107         
4108         
4109         return cfg;
4110     },
4111     initEvents: function() 
4112     {
4113         if (typeof (this.menu) != 'undefined') {
4114             this.menu.parentType = this.xtype;
4115             this.menu.triggerEl = this.el;
4116             this.menu = this.addxtype(Roo.apply({}, this.menu));
4117         }
4118         
4119         this.el.select('a',true).on('click', this.onClick, this);
4120         
4121         if(this.tagtype == 'span'){
4122             this.el.select('span',true).on('click', this.onClick, this);
4123         }
4124        
4125         // at this point parent should be available..
4126         this.parent().register(this);
4127     },
4128     
4129     onClick : function(e)
4130     {
4131         if(
4132                 this.preventDefault || 
4133                 this.href == '#' 
4134         ){
4135             
4136             e.preventDefault();
4137         }
4138         
4139         if (this.disabled) {
4140             return;
4141         }
4142         
4143         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144         if (tg && tg.transition) {
4145             Roo.log("waiting for the transitionend");
4146             return;
4147         }
4148         
4149         
4150         
4151         //Roo.log("fire event clicked");
4152         if(this.fireEvent('click', this, e) === false){
4153             return;
4154         };
4155         
4156         if(this.tagtype == 'span'){
4157             return;
4158         }
4159         
4160         //Roo.log(this.href);
4161         var ael = this.el.select('a',true).first();
4162         //Roo.log(ael);
4163         
4164         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167                 return; // ignore... - it's a 'hash' to another page.
4168             }
4169             
4170             e.preventDefault();
4171             this.scrollToElement(e);
4172         }
4173         
4174         
4175         var p =  this.parent();
4176    
4177         if (['tabs','pills'].indexOf(p.type)!==-1) {
4178             if (typeof(p.setActiveItem) !== 'undefined') {
4179                 p.setActiveItem(this);
4180             }
4181         }
4182         
4183         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185             // remove the collapsed menu expand...
4186             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4187         }
4188     },
4189     
4190     isActive: function () {
4191         return this.active
4192     },
4193     setActive : function(state, fire, is_was_active)
4194     {
4195         if (this.active && !state & this.navId) {
4196             this.was_active = true;
4197             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4198             if (nv) {
4199                 nv.clearWasActive(this);
4200             }
4201             
4202         }
4203         this.active = state;
4204         
4205         if (!state ) {
4206             this.el.removeClass('active');
4207         } else if (!this.el.hasClass('active')) {
4208             this.el.addClass('active');
4209         }
4210         if (fire) {
4211             this.fireEvent('changed', this, state);
4212         }
4213         
4214         // show a panel if it's registered and related..
4215         
4216         if (!this.navId || !this.tabId || !state || is_was_active) {
4217             return;
4218         }
4219         
4220         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4221         if (!tg) {
4222             return;
4223         }
4224         var pan = tg.getPanelByName(this.tabId);
4225         if (!pan) {
4226             return;
4227         }
4228         // if we can not flip to new panel - go back to old nav highlight..
4229         if (false == tg.showPanel(pan)) {
4230             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4231             if (nv) {
4232                 var onav = nv.getWasActive();
4233                 if (onav) {
4234                     onav.setActive(true, false, true);
4235                 }
4236             }
4237             
4238         }
4239         
4240         
4241         
4242     },
4243      // this should not be here...
4244     setDisabled : function(state)
4245     {
4246         this.disabled = state;
4247         if (!state ) {
4248             this.el.removeClass('disabled');
4249         } else if (!this.el.hasClass('disabled')) {
4250             this.el.addClass('disabled');
4251         }
4252         
4253     },
4254     
4255     /**
4256      * Fetch the element to display the tooltip on.
4257      * @return {Roo.Element} defaults to this.el
4258      */
4259     tooltipEl : function()
4260     {
4261         return this.el.select('' + this.tagtype + '', true).first();
4262     },
4263     
4264     scrollToElement : function(e)
4265     {
4266         var c = document.body;
4267         
4268         /*
4269          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4270          */
4271         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272             c = document.documentElement;
4273         }
4274         
4275         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4276         
4277         if(!target){
4278             return;
4279         }
4280
4281         var o = target.calcOffsetsTo(c);
4282         
4283         var options = {
4284             target : target,
4285             value : o[1]
4286         }
4287         
4288         this.fireEvent('scrollto', this, options, e);
4289         
4290         Roo.get(c).scrollTo('top', options.value, true);
4291         
4292         return;
4293     }
4294 });
4295  
4296
4297  /*
4298  * - LGPL
4299  *
4300  * sidebar item
4301  *
4302  *  li
4303  *    <span> icon </span>
4304  *    <span> text </span>
4305  *    <span>badge </span>
4306  */
4307
4308 /**
4309  * @class Roo.bootstrap.NavSidebarItem
4310  * @extends Roo.bootstrap.NavItem
4311  * Bootstrap Navbar.NavSidebarItem class
4312  * @constructor
4313  * Create a new Navbar Button
4314  * @param {Object} config The config object
4315  */
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4318     this.addEvents({
4319         // raw events
4320         /**
4321          * @event click
4322          * The raw click event for the entire grid.
4323          * @param {Roo.EventObject} e
4324          */
4325         "click" : true,
4326          /**
4327             * @event changed
4328             * Fires when the active item active state changes
4329             * @param {Roo.bootstrap.NavSidebarItem} this
4330             * @param {boolean} state the new state
4331              
4332          */
4333         'changed': true
4334     });
4335    
4336 };
4337
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4339     
4340     
4341     getAutoCreate : function(){
4342         
4343         
4344         var a = {
4345                 tag: 'a',
4346                 href : this.href || '#',
4347                 cls: '',
4348                 html : '',
4349                 cn : []
4350         };
4351         var cfg = {
4352             tag: 'li',
4353             cls: '',
4354             cn: [ a ]
4355         }
4356         var span = {
4357             tag: 'span',
4358             html : this.html || ''
4359         }
4360         
4361         
4362         if (this.active) {
4363             cfg.cls += ' active';
4364         }
4365         
4366         // left icon..
4367         if (this.glyphicon || this.icon) {
4368             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4369             a.cn.push({ tag : 'i', cls : c }) ;
4370         }
4371         // html..
4372         a.cn.push(span);
4373         // then badge..
4374         if (this.badge !== '') {
4375             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4376         }
4377         // fi
4378         if (this.menu) {
4379             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380             a.cls += 'dropdown-toggle treeview' ;
4381             
4382         }
4383         
4384         
4385         
4386         return cfg;
4387          
4388            
4389     }
4390    
4391      
4392  
4393 });
4394  
4395
4396  /*
4397  * - LGPL
4398  *
4399  * row
4400  * 
4401  */
4402
4403 /**
4404  * @class Roo.bootstrap.Row
4405  * @extends Roo.bootstrap.Component
4406  * Bootstrap Row class (contains columns...)
4407  * 
4408  * @constructor
4409  * Create a new Row
4410  * @param {Object} config The config object
4411  */
4412
4413 Roo.bootstrap.Row = function(config){
4414     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4415 };
4416
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4418     
4419     getAutoCreate : function(){
4420        return {
4421             cls: 'row clearfix'
4422        };
4423     }
4424     
4425     
4426 });
4427
4428  
4429
4430  /*
4431  * - LGPL
4432  *
4433  * element
4434  * 
4435  */
4436
4437 /**
4438  * @class Roo.bootstrap.Element
4439  * @extends Roo.bootstrap.Component
4440  * Bootstrap Element class
4441  * @cfg {String} html contents of the element
4442  * @cfg {String} tag tag of the element
4443  * @cfg {String} cls class of the element
4444  * @cfg {Boolean} preventDefault (true|false) default false
4445  * @cfg {Boolean} clickable (true|false) default false
4446  * 
4447  * @constructor
4448  * Create a new Element
4449  * @param {Object} config The config object
4450  */
4451
4452 Roo.bootstrap.Element = function(config){
4453     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4454     
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * When a element is chick
4460          * @param {Roo.bootstrap.Element} this
4461          * @param {Roo.EventObject} e
4462          */
4463         "click" : true
4464     });
4465 };
4466
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4468     
4469     tag: 'div',
4470     cls: '',
4471     html: '',
4472     preventDefault: false, 
4473     clickable: false,
4474     
4475     getAutoCreate : function(){
4476         
4477         var cfg = {
4478             tag: this.tag,
4479             cls: this.cls,
4480             html: this.html
4481         }
4482         
4483         return cfg;
4484     },
4485     
4486     initEvents: function() 
4487     {
4488         Roo.bootstrap.Element.superclass.initEvents.call(this);
4489         
4490         if(this.clickable){
4491             this.el.on('click', this.onClick, this);
4492         }
4493         
4494     },
4495     
4496     onClick : function(e)
4497     {
4498         if(this.preventDefault){
4499             e.preventDefault();
4500         }
4501         
4502         this.fireEvent('click', this, e);
4503     },
4504     
4505     getValue : function()
4506     {
4507         return this.el.dom.innerHTML;
4508     },
4509     
4510     setValue : function(value)
4511     {
4512         this.el.dom.innerHTML = value;
4513     }
4514    
4515 });
4516
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * pagination
4523  * 
4524  */
4525
4526 /**
4527  * @class Roo.bootstrap.Pagination
4528  * @extends Roo.bootstrap.Component
4529  * Bootstrap Pagination class
4530  * @cfg {String} size xs | sm | md | lg
4531  * @cfg {Boolean} inverse false | true
4532  * 
4533  * @constructor
4534  * Create a new Pagination
4535  * @param {Object} config The config object
4536  */
4537
4538 Roo.bootstrap.Pagination = function(config){
4539     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4540 };
4541
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4543     
4544     cls: false,
4545     size: false,
4546     inverse: false,
4547     
4548     getAutoCreate : function(){
4549         var cfg = {
4550             tag: 'ul',
4551                 cls: 'pagination'
4552         };
4553         if (this.inverse) {
4554             cfg.cls += ' inverse';
4555         }
4556         if (this.html) {
4557             cfg.html=this.html;
4558         }
4559         if (this.cls) {
4560             cfg.cls += " " + this.cls;
4561         }
4562         return cfg;
4563     }
4564    
4565 });
4566
4567  
4568
4569  /*
4570  * - LGPL
4571  *
4572  * Pagination item
4573  * 
4574  */
4575
4576
4577 /**
4578  * @class Roo.bootstrap.PaginationItem
4579  * @extends Roo.bootstrap.Component
4580  * Bootstrap PaginationItem class
4581  * @cfg {String} html text
4582  * @cfg {String} href the link
4583  * @cfg {Boolean} preventDefault (true | false) default true
4584  * @cfg {Boolean} active (true | false) default false
4585  * @cfg {Boolean} disabled default false
4586  * 
4587  * 
4588  * @constructor
4589  * Create a new PaginationItem
4590  * @param {Object} config The config object
4591  */
4592
4593
4594 Roo.bootstrap.PaginationItem = function(config){
4595     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4596     this.addEvents({
4597         // raw events
4598         /**
4599          * @event click
4600          * The raw click event for the entire grid.
4601          * @param {Roo.EventObject} e
4602          */
4603         "click" : true
4604     });
4605 };
4606
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4608     
4609     href : false,
4610     html : false,
4611     preventDefault: true,
4612     active : false,
4613     cls : false,
4614     disabled: false,
4615     
4616     getAutoCreate : function(){
4617         var cfg= {
4618             tag: 'li',
4619             cn: [
4620                 {
4621                     tag : 'a',
4622                     href : this.href ? this.href : '#',
4623                     html : this.html ? this.html : ''
4624                 }
4625             ]
4626         };
4627         
4628         if(this.cls){
4629             cfg.cls = this.cls;
4630         }
4631         
4632         if(this.disabled){
4633             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4634         }
4635         
4636         if(this.active){
4637             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4638         }
4639         
4640         return cfg;
4641     },
4642     
4643     initEvents: function() {
4644         
4645         this.el.on('click', this.onClick, this);
4646         
4647     },
4648     onClick : function(e)
4649     {
4650         Roo.log('PaginationItem on click ');
4651         if(this.preventDefault){
4652             e.preventDefault();
4653         }
4654         
4655         if(this.disabled){
4656             return;
4657         }
4658         
4659         this.fireEvent('click', this, e);
4660     }
4661    
4662 });
4663
4664  
4665
4666  /*
4667  * - LGPL
4668  *
4669  * slider
4670  * 
4671  */
4672
4673
4674 /**
4675  * @class Roo.bootstrap.Slider
4676  * @extends Roo.bootstrap.Component
4677  * Bootstrap Slider class
4678  *    
4679  * @constructor
4680  * Create a new Slider
4681  * @param {Object} config The config object
4682  */
4683
4684 Roo.bootstrap.Slider = function(config){
4685     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4686 };
4687
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4689     
4690     getAutoCreate : function(){
4691         
4692         var cfg = {
4693             tag: 'div',
4694             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4695             cn: [
4696                 {
4697                     tag: 'a',
4698                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4699                 }
4700             ]
4701         }
4702         
4703         return cfg;
4704     }
4705    
4706 });
4707
4708  /*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718  
4719
4720 /**
4721  * @class Roo.grid.ColumnModel
4722  * @extends Roo.util.Observable
4723  * This is the default implementation of a ColumnModel used by the Grid. It defines
4724  * the columns in the grid.
4725  * <br>Usage:<br>
4726  <pre><code>
4727  var colModel = new Roo.grid.ColumnModel([
4728         {header: "Ticker", width: 60, sortable: true, locked: true},
4729         {header: "Company Name", width: 150, sortable: true},
4730         {header: "Market Cap.", width: 100, sortable: true},
4731         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732         {header: "Employees", width: 100, sortable: true, resizable: false}
4733  ]);
4734  </code></pre>
4735  * <p>
4736  
4737  * The config options listed for this class are options which may appear in each
4738  * individual column definition.
4739  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4740  * @constructor
4741  * @param {Object} config An Array of column config objects. See this class's
4742  * config objects for details.
4743 */
4744 Roo.grid.ColumnModel = function(config){
4745         /**
4746      * The config passed into the constructor
4747      */
4748     this.config = config;
4749     this.lookup = {};
4750
4751     // if no id, create one
4752     // if the column does not have a dataIndex mapping,
4753     // map it to the order it is in the config
4754     for(var i = 0, len = config.length; i < len; i++){
4755         var c = config[i];
4756         if(typeof c.dataIndex == "undefined"){
4757             c.dataIndex = i;
4758         }
4759         if(typeof c.renderer == "string"){
4760             c.renderer = Roo.util.Format[c.renderer];
4761         }
4762         if(typeof c.id == "undefined"){
4763             c.id = Roo.id();
4764         }
4765         if(c.editor && c.editor.xtype){
4766             c.editor  = Roo.factory(c.editor, Roo.grid);
4767         }
4768         if(c.editor && c.editor.isFormField){
4769             c.editor = new Roo.grid.GridEditor(c.editor);
4770         }
4771         this.lookup[c.id] = c;
4772     }
4773
4774     /**
4775      * The width of columns which have no width specified (defaults to 100)
4776      * @type Number
4777      */
4778     this.defaultWidth = 100;
4779
4780     /**
4781      * Default sortable of columns which have no sortable specified (defaults to false)
4782      * @type Boolean
4783      */
4784     this.defaultSortable = false;
4785
4786     this.addEvents({
4787         /**
4788              * @event widthchange
4789              * Fires when the width of a column changes.
4790              * @param {ColumnModel} this
4791              * @param {Number} columnIndex The column index
4792              * @param {Number} newWidth The new width
4793              */
4794             "widthchange": true,
4795         /**
4796              * @event headerchange
4797              * Fires when the text of a header changes.
4798              * @param {ColumnModel} this
4799              * @param {Number} columnIndex The column index
4800              * @param {Number} newText The new header text
4801              */
4802             "headerchange": true,
4803         /**
4804              * @event hiddenchange
4805              * Fires when a column is hidden or "unhidden".
4806              * @param {ColumnModel} this
4807              * @param {Number} columnIndex The column index
4808              * @param {Boolean} hidden true if hidden, false otherwise
4809              */
4810             "hiddenchange": true,
4811             /**
4812          * @event columnmoved
4813          * Fires when a column is moved.
4814          * @param {ColumnModel} this
4815          * @param {Number} oldIndex
4816          * @param {Number} newIndex
4817          */
4818         "columnmoved" : true,
4819         /**
4820          * @event columlockchange
4821          * Fires when a column's locked state is changed
4822          * @param {ColumnModel} this
4823          * @param {Number} colIndex
4824          * @param {Boolean} locked true if locked
4825          */
4826         "columnlockchange" : true
4827     });
4828     Roo.grid.ColumnModel.superclass.constructor.call(this);
4829 };
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4831     /**
4832      * @cfg {String} header The header text to display in the Grid view.
4833      */
4834     /**
4835      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837      * specified, the column's index is used as an index into the Record's data Array.
4838      */
4839     /**
4840      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4842      */
4843     /**
4844      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845      * Defaults to the value of the {@link #defaultSortable} property.
4846      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4847      */
4848     /**
4849      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4850      */
4851     /**
4852      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4853      */
4854     /**
4855      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4856      */
4857     /**
4858      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4859      */
4860     /**
4861      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4865      */
4866        /**
4867      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4868      */
4869     /**
4870      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4871      */
4872     /**
4873      * @cfg {String} cursor (Optional)
4874      */
4875     /**
4876      * @cfg {String} tooltip (Optional)
4877      */
4878     /**
4879      * Returns the id of the column at the specified index.
4880      * @param {Number} index The column index
4881      * @return {String} the id
4882      */
4883     getColumnId : function(index){
4884         return this.config[index].id;
4885     },
4886
4887     /**
4888      * Returns the column for a specified id.
4889      * @param {String} id The column id
4890      * @return {Object} the column
4891      */
4892     getColumnById : function(id){
4893         return this.lookup[id];
4894     },
4895
4896     
4897     /**
4898      * Returns the column for a specified dataIndex.
4899      * @param {String} dataIndex The column dataIndex
4900      * @return {Object|Boolean} the column or false if not found
4901      */
4902     getColumnByDataIndex: function(dataIndex){
4903         var index = this.findColumnIndex(dataIndex);
4904         return index > -1 ? this.config[index] : false;
4905     },
4906     
4907     /**
4908      * Returns the index for a specified column id.
4909      * @param {String} id The column id
4910      * @return {Number} the index, or -1 if not found
4911      */
4912     getIndexById : function(id){
4913         for(var i = 0, len = this.config.length; i < len; i++){
4914             if(this.config[i].id == id){
4915                 return i;
4916             }
4917         }
4918         return -1;
4919     },
4920     
4921     /**
4922      * Returns the index for a specified column dataIndex.
4923      * @param {String} dataIndex The column dataIndex
4924      * @return {Number} the index, or -1 if not found
4925      */
4926     
4927     findColumnIndex : function(dataIndex){
4928         for(var i = 0, len = this.config.length; i < len; i++){
4929             if(this.config[i].dataIndex == dataIndex){
4930                 return i;
4931             }
4932         }
4933         return -1;
4934     },
4935     
4936     
4937     moveColumn : function(oldIndex, newIndex){
4938         var c = this.config[oldIndex];
4939         this.config.splice(oldIndex, 1);
4940         this.config.splice(newIndex, 0, c);
4941         this.dataMap = null;
4942         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4943     },
4944
4945     isLocked : function(colIndex){
4946         return this.config[colIndex].locked === true;
4947     },
4948
4949     setLocked : function(colIndex, value, suppressEvent){
4950         if(this.isLocked(colIndex) == value){
4951             return;
4952         }
4953         this.config[colIndex].locked = value;
4954         if(!suppressEvent){
4955             this.fireEvent("columnlockchange", this, colIndex, value);
4956         }
4957     },
4958
4959     getTotalLockedWidth : function(){
4960         var totalWidth = 0;
4961         for(var i = 0; i < this.config.length; i++){
4962             if(this.isLocked(i) && !this.isHidden(i)){
4963                 this.totalWidth += this.getColumnWidth(i);
4964             }
4965         }
4966         return totalWidth;
4967     },
4968
4969     getLockedCount : function(){
4970         for(var i = 0, len = this.config.length; i < len; i++){
4971             if(!this.isLocked(i)){
4972                 return i;
4973             }
4974         }
4975     },
4976
4977     /**
4978      * Returns the number of columns.
4979      * @return {Number}
4980      */
4981     getColumnCount : function(visibleOnly){
4982         if(visibleOnly === true){
4983             var c = 0;
4984             for(var i = 0, len = this.config.length; i < len; i++){
4985                 if(!this.isHidden(i)){
4986                     c++;
4987                 }
4988             }
4989             return c;
4990         }
4991         return this.config.length;
4992     },
4993
4994     /**
4995      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996      * @param {Function} fn
4997      * @param {Object} scope (optional)
4998      * @return {Array} result
4999      */
5000     getColumnsBy : function(fn, scope){
5001         var r = [];
5002         for(var i = 0, len = this.config.length; i < len; i++){
5003             var c = this.config[i];
5004             if(fn.call(scope||this, c, i) === true){
5005                 r[r.length] = c;
5006             }
5007         }
5008         return r;
5009     },
5010
5011     /**
5012      * Returns true if the specified column is sortable.
5013      * @param {Number} col The column index
5014      * @return {Boolean}
5015      */
5016     isSortable : function(col){
5017         if(typeof this.config[col].sortable == "undefined"){
5018             return this.defaultSortable;
5019         }
5020         return this.config[col].sortable;
5021     },
5022
5023     /**
5024      * Returns the rendering (formatting) function defined for the column.
5025      * @param {Number} col The column index.
5026      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5027      */
5028     getRenderer : function(col){
5029         if(!this.config[col].renderer){
5030             return Roo.grid.ColumnModel.defaultRenderer;
5031         }
5032         return this.config[col].renderer;
5033     },
5034
5035     /**
5036      * Sets the rendering (formatting) function for a column.
5037      * @param {Number} col The column index
5038      * @param {Function} fn The function to use to process the cell's raw data
5039      * to return HTML markup for the grid view. The render function is called with
5040      * the following parameters:<ul>
5041      * <li>Data value.</li>
5042      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043      * <li>css A CSS style string to apply to the table cell.</li>
5044      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046      * <li>Row index</li>
5047      * <li>Column index</li>
5048      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5049      */
5050     setRenderer : function(col, fn){
5051         this.config[col].renderer = fn;
5052     },
5053
5054     /**
5055      * Returns the width for the specified column.
5056      * @param {Number} col The column index
5057      * @return {Number}
5058      */
5059     getColumnWidth : function(col){
5060         return this.config[col].width * 1 || this.defaultWidth;
5061     },
5062
5063     /**
5064      * Sets the width for a column.
5065      * @param {Number} col The column index
5066      * @param {Number} width The new width
5067      */
5068     setColumnWidth : function(col, width, suppressEvent){
5069         this.config[col].width = width;
5070         this.totalWidth = null;
5071         if(!suppressEvent){
5072              this.fireEvent("widthchange", this, col, width);
5073         }
5074     },
5075
5076     /**
5077      * Returns the total width of all columns.
5078      * @param {Boolean} includeHidden True to include hidden column widths
5079      * @return {Number}
5080      */
5081     getTotalWidth : function(includeHidden){
5082         if(!this.totalWidth){
5083             this.totalWidth = 0;
5084             for(var i = 0, len = this.config.length; i < len; i++){
5085                 if(includeHidden || !this.isHidden(i)){
5086                     this.totalWidth += this.getColumnWidth(i);
5087                 }
5088             }
5089         }
5090         return this.totalWidth;
5091     },
5092
5093     /**
5094      * Returns the header for the specified column.
5095      * @param {Number} col The column index
5096      * @return {String}
5097      */
5098     getColumnHeader : function(col){
5099         return this.config[col].header;
5100     },
5101
5102     /**
5103      * Sets the header for a column.
5104      * @param {Number} col The column index
5105      * @param {String} header The new header
5106      */
5107     setColumnHeader : function(col, header){
5108         this.config[col].header = header;
5109         this.fireEvent("headerchange", this, col, header);
5110     },
5111
5112     /**
5113      * Returns the tooltip for the specified column.
5114      * @param {Number} col The column index
5115      * @return {String}
5116      */
5117     getColumnTooltip : function(col){
5118             return this.config[col].tooltip;
5119     },
5120     /**
5121      * Sets the tooltip for a column.
5122      * @param {Number} col The column index
5123      * @param {String} tooltip The new tooltip
5124      */
5125     setColumnTooltip : function(col, tooltip){
5126             this.config[col].tooltip = tooltip;
5127     },
5128
5129     /**
5130      * Returns the dataIndex for the specified column.
5131      * @param {Number} col The column index
5132      * @return {Number}
5133      */
5134     getDataIndex : function(col){
5135         return this.config[col].dataIndex;
5136     },
5137
5138     /**
5139      * Sets the dataIndex for a column.
5140      * @param {Number} col The column index
5141      * @param {Number} dataIndex The new dataIndex
5142      */
5143     setDataIndex : function(col, dataIndex){
5144         this.config[col].dataIndex = dataIndex;
5145     },
5146
5147     
5148     
5149     /**
5150      * Returns true if the cell is editable.
5151      * @param {Number} colIndex The column index
5152      * @param {Number} rowIndex The row index
5153      * @return {Boolean}
5154      */
5155     isCellEditable : function(colIndex, rowIndex){
5156         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5157     },
5158
5159     /**
5160      * Returns the editor defined for the cell/column.
5161      * return false or null to disable editing.
5162      * @param {Number} colIndex The column index
5163      * @param {Number} rowIndex The row index
5164      * @return {Object}
5165      */
5166     getCellEditor : function(colIndex, rowIndex){
5167         return this.config[colIndex].editor;
5168     },
5169
5170     /**
5171      * Sets if a column is editable.
5172      * @param {Number} col The column index
5173      * @param {Boolean} editable True if the column is editable
5174      */
5175     setEditable : function(col, editable){
5176         this.config[col].editable = editable;
5177     },
5178
5179
5180     /**
5181      * Returns true if the column is hidden.
5182      * @param {Number} colIndex The column index
5183      * @return {Boolean}
5184      */
5185     isHidden : function(colIndex){
5186         return this.config[colIndex].hidden;
5187     },
5188
5189
5190     /**
5191      * Returns true if the column width cannot be changed
5192      */
5193     isFixed : function(colIndex){
5194         return this.config[colIndex].fixed;
5195     },
5196
5197     /**
5198      * Returns true if the column can be resized
5199      * @return {Boolean}
5200      */
5201     isResizable : function(colIndex){
5202         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5203     },
5204     /**
5205      * Sets if a column is hidden.
5206      * @param {Number} colIndex The column index
5207      * @param {Boolean} hidden True if the column is hidden
5208      */
5209     setHidden : function(colIndex, hidden){
5210         this.config[colIndex].hidden = hidden;
5211         this.totalWidth = null;
5212         this.fireEvent("hiddenchange", this, colIndex, hidden);
5213     },
5214
5215     /**
5216      * Sets the editor for a column.
5217      * @param {Number} col The column index
5218      * @param {Object} editor The editor object
5219      */
5220     setEditor : function(col, editor){
5221         this.config[col].editor = editor;
5222     }
5223 });
5224
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226         if(typeof value == "string" && value.length < 1){
5227             return "&#160;";
5228         }
5229         return value;
5230 };
5231
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5234 /*
5235  * Based on:
5236  * Ext JS Library 1.1.1
5237  * Copyright(c) 2006-2007, Ext JS, LLC.
5238  *
5239  * Originally Released Under LGPL - original licence link has changed is not relivant.
5240  *
5241  * Fork - LGPL
5242  * <script type="text/javascript">
5243  */
5244  
5245 /**
5246  * @class Roo.LoadMask
5247  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5248  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5250  * element's UpdateManager load indicator and will be destroyed after the initial load.
5251  * @constructor
5252  * Create a new LoadMask
5253  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254  * @param {Object} config The config object
5255  */
5256 Roo.LoadMask = function(el, config){
5257     this.el = Roo.get(el);
5258     Roo.apply(this, config);
5259     if(this.store){
5260         this.store.on('beforeload', this.onBeforeLoad, this);
5261         this.store.on('load', this.onLoad, this);
5262         this.store.on('loadexception', this.onLoadException, this);
5263         this.removeMask = false;
5264     }else{
5265         var um = this.el.getUpdateManager();
5266         um.showLoadIndicator = false; // disable the default indicator
5267         um.on('beforeupdate', this.onBeforeLoad, this);
5268         um.on('update', this.onLoad, this);
5269         um.on('failure', this.onLoad, this);
5270         this.removeMask = true;
5271     }
5272 };
5273
5274 Roo.LoadMask.prototype = {
5275     /**
5276      * @cfg {Boolean} removeMask
5277      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5279      */
5280     /**
5281      * @cfg {String} msg
5282      * The text to display in a centered loading message box (defaults to 'Loading...')
5283      */
5284     msg : 'Loading...',
5285     /**
5286      * @cfg {String} msgCls
5287      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5288      */
5289     msgCls : 'x-mask-loading',
5290
5291     /**
5292      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5293      * @type Boolean
5294      */
5295     disabled: false,
5296
5297     /**
5298      * Disables the mask to prevent it from being displayed
5299      */
5300     disable : function(){
5301        this.disabled = true;
5302     },
5303
5304     /**
5305      * Enables the mask so that it can be displayed
5306      */
5307     enable : function(){
5308         this.disabled = false;
5309     },
5310     
5311     onLoadException : function()
5312     {
5313         Roo.log(arguments);
5314         
5315         if (typeof(arguments[3]) != 'undefined') {
5316             Roo.MessageBox.alert("Error loading",arguments[3]);
5317         } 
5318         /*
5319         try {
5320             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5322             }   
5323         } catch(e) {
5324             
5325         }
5326         */
5327     
5328         
5329         
5330         this.el.unmask(this.removeMask);
5331     },
5332     // private
5333     onLoad : function()
5334     {
5335         this.el.unmask(this.removeMask);
5336     },
5337
5338     // private
5339     onBeforeLoad : function(){
5340         if(!this.disabled){
5341             this.el.mask(this.msg, this.msgCls);
5342         }
5343     },
5344
5345     // private
5346     destroy : function(){
5347         if(this.store){
5348             this.store.un('beforeload', this.onBeforeLoad, this);
5349             this.store.un('load', this.onLoad, this);
5350             this.store.un('loadexception', this.onLoadException, this);
5351         }else{
5352             var um = this.el.getUpdateManager();
5353             um.un('beforeupdate', this.onBeforeLoad, this);
5354             um.un('update', this.onLoad, this);
5355             um.un('failure', this.onLoad, this);
5356         }
5357     }
5358 };/*
5359  * - LGPL
5360  *
5361  * table
5362  * 
5363  */
5364
5365 /**
5366  * @class Roo.bootstrap.Table
5367  * @extends Roo.bootstrap.Component
5368  * Bootstrap Table class
5369  * @cfg {String} cls table class
5370  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371  * @cfg {String} bgcolor Specifies the background color for a table
5372  * @cfg {Number} border Specifies whether the table cells should have borders or not
5373  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374  * @cfg {Number} cellspacing Specifies the space between cells
5375  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377  * @cfg {String} sortable Specifies that the table should be sortable
5378  * @cfg {String} summary Specifies a summary of the content of a table
5379  * @cfg {Number} width Specifies the width of a table
5380  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5381  * 
5382  * @cfg {boolean} striped Should the rows be alternative striped
5383  * @cfg {boolean} bordered Add borders to the table
5384  * @cfg {boolean} hover Add hover highlighting
5385  * @cfg {boolean} condensed Format condensed
5386  * @cfg {boolean} responsive Format condensed
5387  * @cfg {Boolean} loadMask (true|false) default false
5388  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5389  * @cfg {Boolean} thead (true|false) generate thead, default true
5390  * @cfg {Boolean} RowSelection (true|false) default false
5391  * @cfg {Boolean} CellSelection (true|false) default false
5392  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5393  
5394  * 
5395  * @constructor
5396  * Create a new Table
5397  * @param {Object} config The config object
5398  */
5399
5400 Roo.bootstrap.Table = function(config){
5401     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5402     
5403     if (this.sm) {
5404         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5405         this.sm = this.selModel;
5406         this.sm.xmodule = this.xmodule || false;
5407     }
5408     if (this.cm && typeof(this.cm.config) == 'undefined') {
5409         this.colModel = new Roo.grid.ColumnModel(this.cm);
5410         this.cm = this.colModel;
5411         this.cm.xmodule = this.xmodule || false;
5412     }
5413     if (this.store) {
5414         this.store= Roo.factory(this.store, Roo.data);
5415         this.ds = this.store;
5416         this.ds.xmodule = this.xmodule || false;
5417          
5418     }
5419     if (this.footer && this.store) {
5420         this.footer.dataSource = this.ds;
5421         this.footer = Roo.factory(this.footer);
5422     }
5423     
5424     /** @private */
5425     this.addEvents({
5426         /**
5427          * @event cellclick
5428          * Fires when a cell is clicked
5429          * @param {Roo.bootstrap.Table} this
5430          * @param {Roo.Element} el
5431          * @param {Number} rowIndex
5432          * @param {Number} columnIndex
5433          * @param {Roo.EventObject} e
5434          */
5435         "cellclick" : true,
5436         /**
5437          * @event celldblclick
5438          * Fires when a cell is double clicked
5439          * @param {Roo.bootstrap.Table} this
5440          * @param {Roo.Element} el
5441          * @param {Number} rowIndex
5442          * @param {Number} columnIndex
5443          * @param {Roo.EventObject} e
5444          */
5445         "celldblclick" : true,
5446         /**
5447          * @event rowclick
5448          * Fires when a row is clicked
5449          * @param {Roo.bootstrap.Table} this
5450          * @param {Roo.Element} el
5451          * @param {Number} rowIndex
5452          * @param {Roo.EventObject} e
5453          */
5454         "rowclick" : true,
5455         /**
5456          * @event rowdblclick
5457          * Fires when a row is double clicked
5458          * @param {Roo.bootstrap.Table} this
5459          * @param {Roo.Element} el
5460          * @param {Number} rowIndex
5461          * @param {Roo.EventObject} e
5462          */
5463         "rowdblclick" : true,
5464         /**
5465          * @event mouseover
5466          * Fires when a mouseover occur
5467          * @param {Roo.bootstrap.Table} this
5468          * @param {Roo.Element} el
5469          * @param {Number} rowIndex
5470          * @param {Number} columnIndex
5471          * @param {Roo.EventObject} e
5472          */
5473         "mouseover" : true,
5474         /**
5475          * @event mouseout
5476          * Fires when a mouseout occur
5477          * @param {Roo.bootstrap.Table} this
5478          * @param {Roo.Element} el
5479          * @param {Number} rowIndex
5480          * @param {Number} columnIndex
5481          * @param {Roo.EventObject} e
5482          */
5483         "mouseout" : true,
5484         /**
5485          * @event rowclass
5486          * Fires when a row is rendered, so you can change add a style to it.
5487          * @param {Roo.bootstrap.Table} this
5488          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5489          */
5490         'rowclass' : true,
5491           /**
5492          * @event rowsrendered
5493          * Fires when all the  rows have been rendered
5494          * @param {Roo.bootstrap.Table} this
5495          */
5496         'rowsrendered' : true
5497         
5498     });
5499 };
5500
5501 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5502     
5503     cls: false,
5504     align: false,
5505     bgcolor: false,
5506     border: false,
5507     cellpadding: false,
5508     cellspacing: false,
5509     frame: false,
5510     rules: false,
5511     sortable: false,
5512     summary: false,
5513     width: false,
5514     striped : false,
5515     bordered: false,
5516     hover:  false,
5517     condensed : false,
5518     responsive : false,
5519     sm : false,
5520     cm : false,
5521     store : false,
5522     loadMask : false,
5523     tfoot : true,
5524     thead : true,
5525     RowSelection : false,
5526     CellSelection : false,
5527     layout : false,
5528     
5529     // Roo.Element - the tbody
5530     mainBody: false, 
5531     
5532     getAutoCreate : function(){
5533         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5534         
5535         cfg = {
5536             tag: 'table',
5537             cls : 'table',
5538             cn : []
5539         }
5540             
5541         if (this.striped) {
5542             cfg.cls += ' table-striped';
5543         }
5544         
5545         if (this.hover) {
5546             cfg.cls += ' table-hover';
5547         }
5548         if (this.bordered) {
5549             cfg.cls += ' table-bordered';
5550         }
5551         if (this.condensed) {
5552             cfg.cls += ' table-condensed';
5553         }
5554         if (this.responsive) {
5555             cfg.cls += ' table-responsive';
5556         }
5557         
5558         if (this.cls) {
5559             cfg.cls+=  ' ' +this.cls;
5560         }
5561         
5562         // this lot should be simplifed...
5563         
5564         if (this.align) {
5565             cfg.align=this.align;
5566         }
5567         if (this.bgcolor) {
5568             cfg.bgcolor=this.bgcolor;
5569         }
5570         if (this.border) {
5571             cfg.border=this.border;
5572         }
5573         if (this.cellpadding) {
5574             cfg.cellpadding=this.cellpadding;
5575         }
5576         if (this.cellspacing) {
5577             cfg.cellspacing=this.cellspacing;
5578         }
5579         if (this.frame) {
5580             cfg.frame=this.frame;
5581         }
5582         if (this.rules) {
5583             cfg.rules=this.rules;
5584         }
5585         if (this.sortable) {
5586             cfg.sortable=this.sortable;
5587         }
5588         if (this.summary) {
5589             cfg.summary=this.summary;
5590         }
5591         if (this.width) {
5592             cfg.width=this.width;
5593         }
5594         if (this.layout) {
5595             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5596         }
5597         
5598         if(this.store || this.cm){
5599             if(this.thead){
5600                 cfg.cn.push(this.renderHeader());
5601             }
5602             
5603             cfg.cn.push(this.renderBody());
5604             
5605             if(this.tfoot){
5606                 cfg.cn.push(this.renderFooter());
5607             }
5608             
5609             cfg.cls+=  ' TableGrid';
5610         }
5611         
5612         return { cn : [ cfg ] };
5613     },
5614     
5615     initEvents : function()
5616     {   
5617         if(!this.store || !this.cm){
5618             return;
5619         }
5620         
5621         //Roo.log('initEvents with ds!!!!');
5622         
5623         this.mainBody = this.el.select('tbody', true).first();
5624         
5625         
5626         var _this = this;
5627         
5628         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5629             e.on('click', _this.sort, _this);
5630         });
5631         
5632         this.el.on("click", this.onClick, this);
5633         this.el.on("dblclick", this.onDblClick, this);
5634         
5635         // why is this done????? = it breaks dialogs??
5636         //this.parent().el.setStyle('position', 'relative');
5637         
5638         
5639         if (this.footer) {
5640             this.footer.parentId = this.id;
5641             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5642         }
5643         
5644         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5645         
5646         this.store.on('load', this.onLoad, this);
5647         this.store.on('beforeload', this.onBeforeLoad, this);
5648         this.store.on('update', this.onUpdate, this);
5649         this.store.on('add', this.onAdd, this);
5650         
5651     },
5652     
5653     onMouseover : function(e, el)
5654     {
5655         var cell = Roo.get(el);
5656         
5657         if(!cell){
5658             return;
5659         }
5660         
5661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5662             cell = cell.findParent('td', false, true);
5663         }
5664         
5665         var row = cell.findParent('tr', false, true);
5666         var cellIndex = cell.dom.cellIndex;
5667         var rowIndex = row.dom.rowIndex - 1; // start from 0
5668         
5669         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5670         
5671     },
5672     
5673     onMouseout : function(e, el)
5674     {
5675         var cell = Roo.get(el);
5676         
5677         if(!cell){
5678             return;
5679         }
5680         
5681         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5682             cell = cell.findParent('td', false, true);
5683         }
5684         
5685         var row = cell.findParent('tr', false, true);
5686         var cellIndex = cell.dom.cellIndex;
5687         var rowIndex = row.dom.rowIndex - 1; // start from 0
5688         
5689         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5690         
5691     },
5692     
5693     onClick : function(e, el)
5694     {
5695         var cell = Roo.get(el);
5696         
5697         if(!cell || (!this.CellSelection && !this.RowSelection)){
5698             return;
5699         }
5700         
5701         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5702             cell = cell.findParent('td', false, true);
5703         }
5704         
5705         if(!cell || typeof(cell) == 'undefined'){
5706             return;
5707         }
5708         
5709         var row = cell.findParent('tr', false, true);
5710         
5711         if(!row || typeof(row) == 'undefined'){
5712             return;
5713         }
5714         
5715         var cellIndex = cell.dom.cellIndex;
5716         var rowIndex = this.getRowIndex(row);
5717         
5718         if(this.CellSelection){
5719             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5720         }
5721         
5722         if(this.RowSelection){
5723             this.fireEvent('rowclick', this, row, rowIndex, e);
5724         }
5725         
5726         
5727     },
5728     
5729     onDblClick : function(e,el)
5730     {
5731         var cell = Roo.get(el);
5732         
5733         if(!cell || (!this.CellSelection && !this.RowSelection)){
5734             return;
5735         }
5736         
5737         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5738             cell = cell.findParent('td', false, true);
5739         }
5740         
5741         if(!cell || typeof(cell) == 'undefined'){
5742             return;
5743         }
5744         
5745         var row = cell.findParent('tr', false, true);
5746         
5747         if(!row || typeof(row) == 'undefined'){
5748             return;
5749         }
5750         
5751         var cellIndex = cell.dom.cellIndex;
5752         var rowIndex = this.getRowIndex(row);
5753         
5754         if(this.CellSelection){
5755             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5756         }
5757         
5758         if(this.RowSelection){
5759             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5760         }
5761     },
5762     
5763     sort : function(e,el)
5764     {
5765         var col = Roo.get(el);
5766         
5767         if(!col.hasClass('sortable')){
5768             return;
5769         }
5770         
5771         var sort = col.attr('sort');
5772         var dir = 'ASC';
5773         
5774         if(col.hasClass('glyphicon-arrow-up')){
5775             dir = 'DESC';
5776         }
5777         
5778         this.store.sortInfo = {field : sort, direction : dir};
5779         
5780         if (this.footer) {
5781             Roo.log("calling footer first");
5782             this.footer.onClick('first');
5783         } else {
5784         
5785             this.store.load({ params : { start : 0 } });
5786         }
5787     },
5788     
5789     renderHeader : function()
5790     {
5791         var header = {
5792             tag: 'thead',
5793             cn : []
5794         };
5795         
5796         var cm = this.cm;
5797         
5798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5799             
5800             var config = cm.config[i];
5801                     
5802             var c = {
5803                 tag: 'th',
5804                 style : '',
5805                 html: cm.getColumnHeader(i)
5806             };
5807             
5808             if(typeof(config.tooltip) != 'undefined'){
5809                 c.tooltip = config.tooltip;
5810             }
5811             
5812             if(typeof(config.colspan) != 'undefined'){
5813                 c.colspan = config.colspan;
5814             }
5815             
5816             if(typeof(config.hidden) != 'undefined' && config.hidden){
5817                 c.style += ' display:none;';
5818             }
5819             
5820             if(typeof(config.dataIndex) != 'undefined'){
5821                 c.sort = config.dataIndex;
5822             }
5823             
5824             if(typeof(config.sortable) != 'undefined' && config.sortable){
5825                 c.cls = 'sortable';
5826             }
5827             
5828             if(typeof(config.align) != 'undefined' && config.align.length){
5829                 c.style += ' text-align:' + config.align + ';';
5830             }
5831             
5832             if(typeof(config.width) != 'undefined'){
5833                 c.style += ' width:' + config.width + 'px;';
5834             }
5835             
5836             if(typeof(config.cls) != 'undefined'){
5837                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5838             }
5839             
5840             header.cn.push(c)
5841         }
5842         
5843         return header;
5844     },
5845     
5846     renderBody : function()
5847     {
5848         var body = {
5849             tag: 'tbody',
5850             cn : [
5851                 {
5852                     tag: 'tr',
5853                     cn : [
5854                         {
5855                             tag : 'td',
5856                             colspan :  this.cm.getColumnCount()
5857                         }
5858                     ]
5859                 }
5860             ]
5861         };
5862         
5863         return body;
5864     },
5865     
5866     renderFooter : function()
5867     {
5868         var footer = {
5869             tag: 'tfoot',
5870             cn : [
5871                 {
5872                     tag: 'tr',
5873                     cn : [
5874                         {
5875                             tag : 'td',
5876                             colspan :  this.cm.getColumnCount()
5877                         }
5878                     ]
5879                 }
5880             ]
5881         };
5882         
5883         return footer;
5884     },
5885     
5886     
5887     
5888     onLoad : function()
5889     {
5890         Roo.log('ds onload');
5891         this.clear();
5892         
5893         var _this = this;
5894         var cm = this.cm;
5895         var ds = this.store;
5896         
5897         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5898             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5899             
5900             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5901                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5902             }
5903             
5904             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5905                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5906             }
5907         });
5908         
5909         var tbody =  this.mainBody;
5910               
5911         if(ds.getCount() > 0){
5912             ds.data.each(function(d,rowIndex){
5913                 var row =  this.renderRow(cm, ds, rowIndex);
5914                 
5915                 tbody.createChild(row);
5916                 
5917                 var _this = this;
5918                 
5919                 if(row.cellObjects.length){
5920                     Roo.each(row.cellObjects, function(r){
5921                         _this.renderCellObject(r);
5922                     })
5923                 }
5924                 
5925             }, this);
5926         }
5927         
5928         Roo.each(this.el.select('tbody td', true).elements, function(e){
5929             e.on('mouseover', _this.onMouseover, _this);
5930         });
5931         
5932         Roo.each(this.el.select('tbody td', true).elements, function(e){
5933             e.on('mouseout', _this.onMouseout, _this);
5934         });
5935         this.fireEvent('rowsrendered', this);
5936         //if(this.loadMask){
5937         //    this.maskEl.hide();
5938         //}
5939     },
5940     
5941     
5942     onUpdate : function(ds,record)
5943     {
5944         this.refreshRow(record);
5945     },
5946     
5947     onRemove : function(ds, record, index, isUpdate){
5948         if(isUpdate !== true){
5949             this.fireEvent("beforerowremoved", this, index, record);
5950         }
5951         var bt = this.mainBody.dom;
5952         
5953         var rows = this.el.select('tbody > tr', true).elements;
5954         
5955         if(typeof(rows[index]) != 'undefined'){
5956             bt.removeChild(rows[index].dom);
5957         }
5958         
5959 //        if(bt.rows[index]){
5960 //            bt.removeChild(bt.rows[index]);
5961 //        }
5962         
5963         if(isUpdate !== true){
5964             //this.stripeRows(index);
5965             //this.syncRowHeights(index, index);
5966             //this.layout();
5967             this.fireEvent("rowremoved", this, index, record);
5968         }
5969     },
5970     
5971     onAdd : function(ds, records, rowIndex)
5972     {
5973         //Roo.log('on Add called');
5974         // - note this does not handle multiple adding very well..
5975         var bt = this.mainBody.dom;
5976         for (var i =0 ; i < records.length;i++) {
5977             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5978             //Roo.log(records[i]);
5979             //Roo.log(this.store.getAt(rowIndex+i));
5980             this.insertRow(this.store, rowIndex + i, false);
5981             return;
5982         }
5983         
5984     },
5985     
5986     
5987     refreshRow : function(record){
5988         var ds = this.store, index;
5989         if(typeof record == 'number'){
5990             index = record;
5991             record = ds.getAt(index);
5992         }else{
5993             index = ds.indexOf(record);
5994         }
5995         this.insertRow(ds, index, true);
5996         this.onRemove(ds, record, index+1, true);
5997         //this.syncRowHeights(index, index);
5998         //this.layout();
5999         this.fireEvent("rowupdated", this, index, record);
6000     },
6001     
6002     insertRow : function(dm, rowIndex, isUpdate){
6003         
6004         if(!isUpdate){
6005             this.fireEvent("beforerowsinserted", this, rowIndex);
6006         }
6007             //var s = this.getScrollState();
6008         var row = this.renderRow(this.cm, this.store, rowIndex);
6009         // insert before rowIndex..
6010         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6011         
6012         var _this = this;
6013                 
6014         if(row.cellObjects.length){
6015             Roo.each(row.cellObjects, function(r){
6016                 _this.renderCellObject(r);
6017             })
6018         }
6019             
6020         if(!isUpdate){
6021             this.fireEvent("rowsinserted", this, rowIndex);
6022             //this.syncRowHeights(firstRow, lastRow);
6023             //this.stripeRows(firstRow);
6024             //this.layout();
6025         }
6026         
6027     },
6028     
6029     
6030     getRowDom : function(rowIndex)
6031     {
6032         var rows = this.el.select('tbody > tr', true).elements;
6033         
6034         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6035         
6036     },
6037     // returns the object tree for a tr..
6038   
6039     
6040     renderRow : function(cm, ds, rowIndex) 
6041     {
6042         
6043         var d = ds.getAt(rowIndex);
6044         
6045         var row = {
6046             tag : 'tr',
6047             cn : []
6048         };
6049             
6050         var cellObjects = [];
6051         
6052         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6053             var config = cm.config[i];
6054             
6055             var renderer = cm.getRenderer(i);
6056             var value = '';
6057             var id = false;
6058             
6059             if(typeof(renderer) !== 'undefined'){
6060                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6061             }
6062             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6063             // and are rendered into the cells after the row is rendered - using the id for the element.
6064             
6065             if(typeof(value) === 'object'){
6066                 id = Roo.id();
6067                 cellObjects.push({
6068                     container : id,
6069                     cfg : value 
6070                 })
6071             }
6072             
6073             var rowcfg = {
6074                 record: d,
6075                 rowIndex : rowIndex,
6076                 colIndex : i,
6077                 rowClass : ''
6078             }
6079
6080             this.fireEvent('rowclass', this, rowcfg);
6081             
6082             var td = {
6083                 tag: 'td',
6084                 cls : rowcfg.rowClass,
6085                 style: '',
6086                 html: (typeof(value) === 'object') ? '' : value
6087             };
6088             
6089             if (id) {
6090                 td.id = id;
6091             }
6092             
6093             if(typeof(config.colspan) != 'undefined'){
6094                 td.colspan = config.colspan;
6095             }
6096             
6097             if(typeof(config.hidden) != 'undefined' && config.hidden){
6098                 td.style += ' display:none;';
6099             }
6100             
6101             if(typeof(config.align) != 'undefined' && config.align.length){
6102                 td.style += ' text-align:' + config.align + ';';
6103             }
6104             
6105             if(typeof(config.width) != 'undefined'){
6106                 td.style += ' width:' +  config.width + 'px;';
6107             }
6108             
6109             if(typeof(config.cursor) != 'undefined'){
6110                 td.style += ' cursor:' +  config.cursor + ';';
6111             }
6112             
6113             if(typeof(config.cls) != 'undefined'){
6114                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6115             }
6116              
6117             row.cn.push(td);
6118            
6119         }
6120         
6121         row.cellObjects = cellObjects;
6122         
6123         return row;
6124           
6125     },
6126     
6127     
6128     
6129     onBeforeLoad : function()
6130     {
6131         //Roo.log('ds onBeforeLoad');
6132         
6133         //this.clear();
6134         
6135         //if(this.loadMask){
6136         //    this.maskEl.show();
6137         //}
6138     },
6139      /**
6140      * Remove all rows
6141      */
6142     clear : function()
6143     {
6144         this.el.select('tbody', true).first().dom.innerHTML = '';
6145     },
6146     /**
6147      * Show or hide a row.
6148      * @param {Number} rowIndex to show or hide
6149      * @param {Boolean} state hide
6150      */
6151     setRowVisibility : function(rowIndex, state)
6152     {
6153         var bt = this.mainBody.dom;
6154         
6155         var rows = this.el.select('tbody > tr', true).elements;
6156         
6157         if(typeof(rows[rowIndex]) == 'undefined'){
6158             return;
6159         }
6160         rows[rowIndex].dom.style.display = state ? '' : 'none';
6161     },
6162     
6163     
6164     getSelectionModel : function(){
6165         if(!this.selModel){
6166             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6167         }
6168         return this.selModel;
6169     },
6170     /*
6171      * Render the Roo.bootstrap object from renderder
6172      */
6173     renderCellObject : function(r)
6174     {
6175         var _this = this;
6176         
6177         var t = r.cfg.render(r.container);
6178         
6179         if(r.cfg.cn){
6180             Roo.each(r.cfg.cn, function(c){
6181                 var child = {
6182                     container: t.getChildContainer(),
6183                     cfg: c
6184                 }
6185                 _this.renderCellObject(child);
6186             })
6187         }
6188     },
6189     
6190     getRowIndex : function(row)
6191     {
6192         var rowIndex = -1;
6193         
6194         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6195             if(el != row){
6196                 return;
6197             }
6198             
6199             rowIndex = index;
6200         });
6201         
6202         return rowIndex;
6203     }
6204    
6205 });
6206
6207  
6208
6209  /*
6210  * - LGPL
6211  *
6212  * table cell
6213  * 
6214  */
6215
6216 /**
6217  * @class Roo.bootstrap.TableCell
6218  * @extends Roo.bootstrap.Component
6219  * Bootstrap TableCell class
6220  * @cfg {String} html cell contain text
6221  * @cfg {String} cls cell class
6222  * @cfg {String} tag cell tag (td|th) default td
6223  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6224  * @cfg {String} align Aligns the content in a cell
6225  * @cfg {String} axis Categorizes cells
6226  * @cfg {String} bgcolor Specifies the background color of a cell
6227  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6228  * @cfg {Number} colspan Specifies the number of columns a cell should span
6229  * @cfg {String} headers Specifies one or more header cells a cell is related to
6230  * @cfg {Number} height Sets the height of a cell
6231  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6232  * @cfg {Number} rowspan Sets the number of rows a cell should span
6233  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6234  * @cfg {String} valign Vertical aligns the content in a cell
6235  * @cfg {Number} width Specifies the width of a cell
6236  * 
6237  * @constructor
6238  * Create a new TableCell
6239  * @param {Object} config The config object
6240  */
6241
6242 Roo.bootstrap.TableCell = function(config){
6243     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6244 };
6245
6246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6247     
6248     html: false,
6249     cls: false,
6250     tag: false,
6251     abbr: false,
6252     align: false,
6253     axis: false,
6254     bgcolor: false,
6255     charoff: false,
6256     colspan: false,
6257     headers: false,
6258     height: false,
6259     nowrap: false,
6260     rowspan: false,
6261     scope: false,
6262     valign: false,
6263     width: false,
6264     
6265     
6266     getAutoCreate : function(){
6267         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6268         
6269         cfg = {
6270             tag: 'td'
6271         }
6272         
6273         if(this.tag){
6274             cfg.tag = this.tag;
6275         }
6276         
6277         if (this.html) {
6278             cfg.html=this.html
6279         }
6280         if (this.cls) {
6281             cfg.cls=this.cls
6282         }
6283         if (this.abbr) {
6284             cfg.abbr=this.abbr
6285         }
6286         if (this.align) {
6287             cfg.align=this.align
6288         }
6289         if (this.axis) {
6290             cfg.axis=this.axis
6291         }
6292         if (this.bgcolor) {
6293             cfg.bgcolor=this.bgcolor
6294         }
6295         if (this.charoff) {
6296             cfg.charoff=this.charoff
6297         }
6298         if (this.colspan) {
6299             cfg.colspan=this.colspan
6300         }
6301         if (this.headers) {
6302             cfg.headers=this.headers
6303         }
6304         if (this.height) {
6305             cfg.height=this.height
6306         }
6307         if (this.nowrap) {
6308             cfg.nowrap=this.nowrap
6309         }
6310         if (this.rowspan) {
6311             cfg.rowspan=this.rowspan
6312         }
6313         if (this.scope) {
6314             cfg.scope=this.scope
6315         }
6316         if (this.valign) {
6317             cfg.valign=this.valign
6318         }
6319         if (this.width) {
6320             cfg.width=this.width
6321         }
6322         
6323         
6324         return cfg;
6325     }
6326    
6327 });
6328
6329  
6330
6331  /*
6332  * - LGPL
6333  *
6334  * table row
6335  * 
6336  */
6337
6338 /**
6339  * @class Roo.bootstrap.TableRow
6340  * @extends Roo.bootstrap.Component
6341  * Bootstrap TableRow class
6342  * @cfg {String} cls row class
6343  * @cfg {String} align Aligns the content in a table row
6344  * @cfg {String} bgcolor Specifies a background color for a table row
6345  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6346  * @cfg {String} valign Vertical aligns the content in a table row
6347  * 
6348  * @constructor
6349  * Create a new TableRow
6350  * @param {Object} config The config object
6351  */
6352
6353 Roo.bootstrap.TableRow = function(config){
6354     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6355 };
6356
6357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6358     
6359     cls: false,
6360     align: false,
6361     bgcolor: false,
6362     charoff: false,
6363     valign: false,
6364     
6365     getAutoCreate : function(){
6366         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6367         
6368         cfg = {
6369             tag: 'tr'
6370         }
6371             
6372         if(this.cls){
6373             cfg.cls = this.cls;
6374         }
6375         if(this.align){
6376             cfg.align = this.align;
6377         }
6378         if(this.bgcolor){
6379             cfg.bgcolor = this.bgcolor;
6380         }
6381         if(this.charoff){
6382             cfg.charoff = this.charoff;
6383         }
6384         if(this.valign){
6385             cfg.valign = this.valign;
6386         }
6387         
6388         return cfg;
6389     }
6390    
6391 });
6392
6393  
6394
6395  /*
6396  * - LGPL
6397  *
6398  * table body
6399  * 
6400  */
6401
6402 /**
6403  * @class Roo.bootstrap.TableBody
6404  * @extends Roo.bootstrap.Component
6405  * Bootstrap TableBody class
6406  * @cfg {String} cls element class
6407  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6408  * @cfg {String} align Aligns the content inside the element
6409  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6410  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6411  * 
6412  * @constructor
6413  * Create a new TableBody
6414  * @param {Object} config The config object
6415  */
6416
6417 Roo.bootstrap.TableBody = function(config){
6418     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6419 };
6420
6421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6422     
6423     cls: false,
6424     tag: false,
6425     align: false,
6426     charoff: false,
6427     valign: false,
6428     
6429     getAutoCreate : function(){
6430         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6431         
6432         cfg = {
6433             tag: 'tbody'
6434         }
6435             
6436         if (this.cls) {
6437             cfg.cls=this.cls
6438         }
6439         if(this.tag){
6440             cfg.tag = this.tag;
6441         }
6442         
6443         if(this.align){
6444             cfg.align = this.align;
6445         }
6446         if(this.charoff){
6447             cfg.charoff = this.charoff;
6448         }
6449         if(this.valign){
6450             cfg.valign = this.valign;
6451         }
6452         
6453         return cfg;
6454     }
6455     
6456     
6457 //    initEvents : function()
6458 //    {
6459 //        
6460 //        if(!this.store){
6461 //            return;
6462 //        }
6463 //        
6464 //        this.store = Roo.factory(this.store, Roo.data);
6465 //        this.store.on('load', this.onLoad, this);
6466 //        
6467 //        this.store.load();
6468 //        
6469 //    },
6470 //    
6471 //    onLoad: function () 
6472 //    {   
6473 //        this.fireEvent('load', this);
6474 //    }
6475 //    
6476 //   
6477 });
6478
6479  
6480
6481  /*
6482  * Based on:
6483  * Ext JS Library 1.1.1
6484  * Copyright(c) 2006-2007, Ext JS, LLC.
6485  *
6486  * Originally Released Under LGPL - original licence link has changed is not relivant.
6487  *
6488  * Fork - LGPL
6489  * <script type="text/javascript">
6490  */
6491
6492 // as we use this in bootstrap.
6493 Roo.namespace('Roo.form');
6494  /**
6495  * @class Roo.form.Action
6496  * Internal Class used to handle form actions
6497  * @constructor
6498  * @param {Roo.form.BasicForm} el The form element or its id
6499  * @param {Object} config Configuration options
6500  */
6501
6502  
6503  
6504 // define the action interface
6505 Roo.form.Action = function(form, options){
6506     this.form = form;
6507     this.options = options || {};
6508 };
6509 /**
6510  * Client Validation Failed
6511  * @const 
6512  */
6513 Roo.form.Action.CLIENT_INVALID = 'client';
6514 /**
6515  * Server Validation Failed
6516  * @const 
6517  */
6518 Roo.form.Action.SERVER_INVALID = 'server';
6519  /**
6520  * Connect to Server Failed
6521  * @const 
6522  */
6523 Roo.form.Action.CONNECT_FAILURE = 'connect';
6524 /**
6525  * Reading Data from Server Failed
6526  * @const 
6527  */
6528 Roo.form.Action.LOAD_FAILURE = 'load';
6529
6530 Roo.form.Action.prototype = {
6531     type : 'default',
6532     failureType : undefined,
6533     response : undefined,
6534     result : undefined,
6535
6536     // interface method
6537     run : function(options){
6538
6539     },
6540
6541     // interface method
6542     success : function(response){
6543
6544     },
6545
6546     // interface method
6547     handleResponse : function(response){
6548
6549     },
6550
6551     // default connection failure
6552     failure : function(response){
6553         
6554         this.response = response;
6555         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6556         this.form.afterAction(this, false);
6557     },
6558
6559     processResponse : function(response){
6560         this.response = response;
6561         if(!response.responseText){
6562             return true;
6563         }
6564         this.result = this.handleResponse(response);
6565         return this.result;
6566     },
6567
6568     // utility functions used internally
6569     getUrl : function(appendParams){
6570         var url = this.options.url || this.form.url || this.form.el.dom.action;
6571         if(appendParams){
6572             var p = this.getParams();
6573             if(p){
6574                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6575             }
6576         }
6577         return url;
6578     },
6579
6580     getMethod : function(){
6581         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6582     },
6583
6584     getParams : function(){
6585         var bp = this.form.baseParams;
6586         var p = this.options.params;
6587         if(p){
6588             if(typeof p == "object"){
6589                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6590             }else if(typeof p == 'string' && bp){
6591                 p += '&' + Roo.urlEncode(bp);
6592             }
6593         }else if(bp){
6594             p = Roo.urlEncode(bp);
6595         }
6596         return p;
6597     },
6598
6599     createCallback : function(){
6600         return {
6601             success: this.success,
6602             failure: this.failure,
6603             scope: this,
6604             timeout: (this.form.timeout*1000),
6605             upload: this.form.fileUpload ? this.success : undefined
6606         };
6607     }
6608 };
6609
6610 Roo.form.Action.Submit = function(form, options){
6611     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6612 };
6613
6614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6615     type : 'submit',
6616
6617     haveProgress : false,
6618     uploadComplete : false,
6619     
6620     // uploadProgress indicator.
6621     uploadProgress : function()
6622     {
6623         if (!this.form.progressUrl) {
6624             return;
6625         }
6626         
6627         if (!this.haveProgress) {
6628             Roo.MessageBox.progress("Uploading", "Uploading");
6629         }
6630         if (this.uploadComplete) {
6631            Roo.MessageBox.hide();
6632            return;
6633         }
6634         
6635         this.haveProgress = true;
6636    
6637         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6638         
6639         var c = new Roo.data.Connection();
6640         c.request({
6641             url : this.form.progressUrl,
6642             params: {
6643                 id : uid
6644             },
6645             method: 'GET',
6646             success : function(req){
6647                //console.log(data);
6648                 var rdata = false;
6649                 var edata;
6650                 try  {
6651                    rdata = Roo.decode(req.responseText)
6652                 } catch (e) {
6653                     Roo.log("Invalid data from server..");
6654                     Roo.log(edata);
6655                     return;
6656                 }
6657                 if (!rdata || !rdata.success) {
6658                     Roo.log(rdata);
6659                     Roo.MessageBox.alert(Roo.encode(rdata));
6660                     return;
6661                 }
6662                 var data = rdata.data;
6663                 
6664                 if (this.uploadComplete) {
6665                    Roo.MessageBox.hide();
6666                    return;
6667                 }
6668                    
6669                 if (data){
6670                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6671                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6672                     );
6673                 }
6674                 this.uploadProgress.defer(2000,this);
6675             },
6676        
6677             failure: function(data) {
6678                 Roo.log('progress url failed ');
6679                 Roo.log(data);
6680             },
6681             scope : this
6682         });
6683            
6684     },
6685     
6686     
6687     run : function()
6688     {
6689         // run get Values on the form, so it syncs any secondary forms.
6690         this.form.getValues();
6691         
6692         var o = this.options;
6693         var method = this.getMethod();
6694         var isPost = method == 'POST';
6695         if(o.clientValidation === false || this.form.isValid()){
6696             
6697             if (this.form.progressUrl) {
6698                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6699                     (new Date() * 1) + '' + Math.random());
6700                     
6701             } 
6702             
6703             
6704             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6705                 form:this.form.el.dom,
6706                 url:this.getUrl(!isPost),
6707                 method: method,
6708                 params:isPost ? this.getParams() : null,
6709                 isUpload: this.form.fileUpload
6710             }));
6711             
6712             this.uploadProgress();
6713
6714         }else if (o.clientValidation !== false){ // client validation failed
6715             this.failureType = Roo.form.Action.CLIENT_INVALID;
6716             this.form.afterAction(this, false);
6717         }
6718     },
6719
6720     success : function(response)
6721     {
6722         this.uploadComplete= true;
6723         if (this.haveProgress) {
6724             Roo.MessageBox.hide();
6725         }
6726         
6727         
6728         var result = this.processResponse(response);
6729         if(result === true || result.success){
6730             this.form.afterAction(this, true);
6731             return;
6732         }
6733         if(result.errors){
6734             this.form.markInvalid(result.errors);
6735             this.failureType = Roo.form.Action.SERVER_INVALID;
6736         }
6737         this.form.afterAction(this, false);
6738     },
6739     failure : function(response)
6740     {
6741         this.uploadComplete= true;
6742         if (this.haveProgress) {
6743             Roo.MessageBox.hide();
6744         }
6745         
6746         this.response = response;
6747         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6748         this.form.afterAction(this, false);
6749     },
6750     
6751     handleResponse : function(response){
6752         if(this.form.errorReader){
6753             var rs = this.form.errorReader.read(response);
6754             var errors = [];
6755             if(rs.records){
6756                 for(var i = 0, len = rs.records.length; i < len; i++) {
6757                     var r = rs.records[i];
6758                     errors[i] = r.data;
6759                 }
6760             }
6761             if(errors.length < 1){
6762                 errors = null;
6763             }
6764             return {
6765                 success : rs.success,
6766                 errors : errors
6767             };
6768         }
6769         var ret = false;
6770         try {
6771             ret = Roo.decode(response.responseText);
6772         } catch (e) {
6773             ret = {
6774                 success: false,
6775                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6776                 errors : []
6777             };
6778         }
6779         return ret;
6780         
6781     }
6782 });
6783
6784
6785 Roo.form.Action.Load = function(form, options){
6786     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6787     this.reader = this.form.reader;
6788 };
6789
6790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6791     type : 'load',
6792
6793     run : function(){
6794         
6795         Roo.Ajax.request(Roo.apply(
6796                 this.createCallback(), {
6797                     method:this.getMethod(),
6798                     url:this.getUrl(false),
6799                     params:this.getParams()
6800         }));
6801     },
6802
6803     success : function(response){
6804         
6805         var result = this.processResponse(response);
6806         if(result === true || !result.success || !result.data){
6807             this.failureType = Roo.form.Action.LOAD_FAILURE;
6808             this.form.afterAction(this, false);
6809             return;
6810         }
6811         this.form.clearInvalid();
6812         this.form.setValues(result.data);
6813         this.form.afterAction(this, true);
6814     },
6815
6816     handleResponse : function(response){
6817         if(this.form.reader){
6818             var rs = this.form.reader.read(response);
6819             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6820             return {
6821                 success : rs.success,
6822                 data : data
6823             };
6824         }
6825         return Roo.decode(response.responseText);
6826     }
6827 });
6828
6829 Roo.form.Action.ACTION_TYPES = {
6830     'load' : Roo.form.Action.Load,
6831     'submit' : Roo.form.Action.Submit
6832 };/*
6833  * - LGPL
6834  *
6835  * form
6836  * 
6837  */
6838
6839 /**
6840  * @class Roo.bootstrap.Form
6841  * @extends Roo.bootstrap.Component
6842  * Bootstrap Form class
6843  * @cfg {String} method  GET | POST (default POST)
6844  * @cfg {String} labelAlign top | left (default top)
6845  * @cfg {String} align left  | right - for navbars
6846  * @cfg {Boolean} loadMask load mask when submit (default true)
6847
6848  * 
6849  * @constructor
6850  * Create a new Form
6851  * @param {Object} config The config object
6852  */
6853
6854
6855 Roo.bootstrap.Form = function(config){
6856     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6857     this.addEvents({
6858         /**
6859          * @event clientvalidation
6860          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6861          * @param {Form} this
6862          * @param {Boolean} valid true if the form has passed client-side validation
6863          */
6864         clientvalidation: true,
6865         /**
6866          * @event beforeaction
6867          * Fires before any action is performed. Return false to cancel the action.
6868          * @param {Form} this
6869          * @param {Action} action The action to be performed
6870          */
6871         beforeaction: true,
6872         /**
6873          * @event actionfailed
6874          * Fires when an action fails.
6875          * @param {Form} this
6876          * @param {Action} action The action that failed
6877          */
6878         actionfailed : true,
6879         /**
6880          * @event actioncomplete
6881          * Fires when an action is completed.
6882          * @param {Form} this
6883          * @param {Action} action The action that completed
6884          */
6885         actioncomplete : true
6886     });
6887     
6888 };
6889
6890 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6891       
6892      /**
6893      * @cfg {String} method
6894      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6895      */
6896     method : 'POST',
6897     /**
6898      * @cfg {String} url
6899      * The URL to use for form actions if one isn't supplied in the action options.
6900      */
6901     /**
6902      * @cfg {Boolean} fileUpload
6903      * Set to true if this form is a file upload.
6904      */
6905      
6906     /**
6907      * @cfg {Object} baseParams
6908      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6909      */
6910       
6911     /**
6912      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6913      */
6914     timeout: 30,
6915     /**
6916      * @cfg {Sting} align (left|right) for navbar forms
6917      */
6918     align : 'left',
6919
6920     // private
6921     activeAction : null,
6922  
6923     /**
6924      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6925      * element by passing it or its id or mask the form itself by passing in true.
6926      * @type Mixed
6927      */
6928     waitMsgTarget : false,
6929     
6930     loadMask : true,
6931     
6932     getAutoCreate : function(){
6933         
6934         var cfg = {
6935             tag: 'form',
6936             method : this.method || 'POST',
6937             id : this.id || Roo.id(),
6938             cls : ''
6939         }
6940         if (this.parent().xtype.match(/^Nav/)) {
6941             cfg.cls = 'navbar-form navbar-' + this.align;
6942             
6943         }
6944         
6945         if (this.labelAlign == 'left' ) {
6946             cfg.cls += ' form-horizontal';
6947         }
6948         
6949         
6950         return cfg;
6951     },
6952     initEvents : function()
6953     {
6954         this.el.on('submit', this.onSubmit, this);
6955         // this was added as random key presses on the form where triggering form submit.
6956         this.el.on('keypress', function(e) {
6957             if (e.getCharCode() != 13) {
6958                 return true;
6959             }
6960             // we might need to allow it for textareas.. and some other items.
6961             // check e.getTarget().
6962             
6963             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6964                 return true;
6965             }
6966         
6967             Roo.log("keypress blocked");
6968             
6969             e.preventDefault();
6970             return false;
6971         });
6972         
6973     },
6974     // private
6975     onSubmit : function(e){
6976         e.stopEvent();
6977     },
6978     
6979      /**
6980      * Returns true if client-side validation on the form is successful.
6981      * @return Boolean
6982      */
6983     isValid : function(){
6984         var items = this.getItems();
6985         var valid = true;
6986         items.each(function(f){
6987            if(!f.validate()){
6988                valid = false;
6989                
6990            }
6991         });
6992         return valid;
6993     },
6994     /**
6995      * Returns true if any fields in this form have changed since their original load.
6996      * @return Boolean
6997      */
6998     isDirty : function(){
6999         var dirty = false;
7000         var items = this.getItems();
7001         items.each(function(f){
7002            if(f.isDirty()){
7003                dirty = true;
7004                return false;
7005            }
7006            return true;
7007         });
7008         return dirty;
7009     },
7010      /**
7011      * Performs a predefined action (submit or load) or custom actions you define on this form.
7012      * @param {String} actionName The name of the action type
7013      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7014      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7015      * accept other config options):
7016      * <pre>
7017 Property          Type             Description
7018 ----------------  ---------------  ----------------------------------------------------------------------------------
7019 url               String           The url for the action (defaults to the form's url)
7020 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7021 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7022 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7023                                    validate the form on the client (defaults to false)
7024      * </pre>
7025      * @return {BasicForm} this
7026      */
7027     doAction : function(action, options){
7028         if(typeof action == 'string'){
7029             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7030         }
7031         if(this.fireEvent('beforeaction', this, action) !== false){
7032             this.beforeAction(action);
7033             action.run.defer(100, action);
7034         }
7035         return this;
7036     },
7037     
7038     // private
7039     beforeAction : function(action){
7040         var o = action.options;
7041         
7042         if(this.loadMask){
7043             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7044         }
7045         // not really supported yet.. ??
7046         
7047         //if(this.waitMsgTarget === true){
7048         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7049         //}else if(this.waitMsgTarget){
7050         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7051         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7052         //}else {
7053         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7054        // }
7055          
7056     },
7057
7058     // private
7059     afterAction : function(action, success){
7060         this.activeAction = null;
7061         var o = action.options;
7062         
7063         //if(this.waitMsgTarget === true){
7064             this.el.unmask();
7065         //}else if(this.waitMsgTarget){
7066         //    this.waitMsgTarget.unmask();
7067         //}else{
7068         //    Roo.MessageBox.updateProgress(1);
7069         //    Roo.MessageBox.hide();
7070        // }
7071         // 
7072         if(success){
7073             if(o.reset){
7074                 this.reset();
7075             }
7076             Roo.callback(o.success, o.scope, [this, action]);
7077             this.fireEvent('actioncomplete', this, action);
7078             
7079         }else{
7080             
7081             // failure condition..
7082             // we have a scenario where updates need confirming.
7083             // eg. if a locking scenario exists..
7084             // we look for { errors : { needs_confirm : true }} in the response.
7085             if (
7086                 (typeof(action.result) != 'undefined')  &&
7087                 (typeof(action.result.errors) != 'undefined')  &&
7088                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7089            ){
7090                 var _t = this;
7091                 Roo.log("not supported yet");
7092                  /*
7093                 
7094                 Roo.MessageBox.confirm(
7095                     "Change requires confirmation",
7096                     action.result.errorMsg,
7097                     function(r) {
7098                         if (r != 'yes') {
7099                             return;
7100                         }
7101                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7102                     }
7103                     
7104                 );
7105                 */
7106                 
7107                 
7108                 return;
7109             }
7110             
7111             Roo.callback(o.failure, o.scope, [this, action]);
7112             // show an error message if no failed handler is set..
7113             if (!this.hasListener('actionfailed')) {
7114                 Roo.log("need to add dialog support");
7115                 /*
7116                 Roo.MessageBox.alert("Error",
7117                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7118                         action.result.errorMsg :
7119                         "Saving Failed, please check your entries or try again"
7120                 );
7121                 */
7122             }
7123             
7124             this.fireEvent('actionfailed', this, action);
7125         }
7126         
7127     },
7128     /**
7129      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7130      * @param {String} id The value to search for
7131      * @return Field
7132      */
7133     findField : function(id){
7134         var items = this.getItems();
7135         var field = items.get(id);
7136         if(!field){
7137              items.each(function(f){
7138                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7139                     field = f;
7140                     return false;
7141                 }
7142                 return true;
7143             });
7144         }
7145         return field || null;
7146     },
7147      /**
7148      * Mark fields in this form invalid in bulk.
7149      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7150      * @return {BasicForm} this
7151      */
7152     markInvalid : function(errors){
7153         if(errors instanceof Array){
7154             for(var i = 0, len = errors.length; i < len; i++){
7155                 var fieldError = errors[i];
7156                 var f = this.findField(fieldError.id);
7157                 if(f){
7158                     f.markInvalid(fieldError.msg);
7159                 }
7160             }
7161         }else{
7162             var field, id;
7163             for(id in errors){
7164                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7165                     field.markInvalid(errors[id]);
7166                 }
7167             }
7168         }
7169         //Roo.each(this.childForms || [], function (f) {
7170         //    f.markInvalid(errors);
7171         //});
7172         
7173         return this;
7174     },
7175
7176     /**
7177      * Set values for fields in this form in bulk.
7178      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7179      * @return {BasicForm} this
7180      */
7181     setValues : function(values){
7182         if(values instanceof Array){ // array of objects
7183             for(var i = 0, len = values.length; i < len; i++){
7184                 var v = values[i];
7185                 var f = this.findField(v.id);
7186                 if(f){
7187                     f.setValue(v.value);
7188                     if(this.trackResetOnLoad){
7189                         f.originalValue = f.getValue();
7190                     }
7191                 }
7192             }
7193         }else{ // object hash
7194             var field, id;
7195             for(id in values){
7196                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7197                     
7198                     if (field.setFromData && 
7199                         field.valueField && 
7200                         field.displayField &&
7201                         // combos' with local stores can 
7202                         // be queried via setValue()
7203                         // to set their value..
7204                         (field.store && !field.store.isLocal)
7205                         ) {
7206                         // it's a combo
7207                         var sd = { };
7208                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7209                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7210                         field.setFromData(sd);
7211                         
7212                     } else {
7213                         field.setValue(values[id]);
7214                     }
7215                     
7216                     
7217                     if(this.trackResetOnLoad){
7218                         field.originalValue = field.getValue();
7219                     }
7220                 }
7221             }
7222         }
7223          
7224         //Roo.each(this.childForms || [], function (f) {
7225         //    f.setValues(values);
7226         //});
7227                 
7228         return this;
7229     },
7230
7231     /**
7232      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7233      * they are returned as an array.
7234      * @param {Boolean} asString
7235      * @return {Object}
7236      */
7237     getValues : function(asString){
7238         //if (this.childForms) {
7239             // copy values from the child forms
7240         //    Roo.each(this.childForms, function (f) {
7241         //        this.setValues(f.getValues());
7242         //    }, this);
7243         //}
7244         
7245         
7246         
7247         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7248         if(asString === true){
7249             return fs;
7250         }
7251         return Roo.urlDecode(fs);
7252     },
7253     
7254     /**
7255      * Returns the fields in this form as an object with key/value pairs. 
7256      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7257      * @return {Object}
7258      */
7259     getFieldValues : function(with_hidden)
7260     {
7261         var items = this.getItems();
7262         var ret = {};
7263         items.each(function(f){
7264             if (!f.getName()) {
7265                 return;
7266             }
7267             var v = f.getValue();
7268             if (f.inputType =='radio') {
7269                 if (typeof(ret[f.getName()]) == 'undefined') {
7270                     ret[f.getName()] = ''; // empty..
7271                 }
7272                 
7273                 if (!f.el.dom.checked) {
7274                     return;
7275                     
7276                 }
7277                 v = f.el.dom.value;
7278                 
7279             }
7280             
7281             // not sure if this supported any more..
7282             if ((typeof(v) == 'object') && f.getRawValue) {
7283                 v = f.getRawValue() ; // dates..
7284             }
7285             // combo boxes where name != hiddenName...
7286             if (f.name != f.getName()) {
7287                 ret[f.name] = f.getRawValue();
7288             }
7289             ret[f.getName()] = v;
7290         });
7291         
7292         return ret;
7293     },
7294
7295     /**
7296      * Clears all invalid messages in this form.
7297      * @return {BasicForm} this
7298      */
7299     clearInvalid : function(){
7300         var items = this.getItems();
7301         
7302         items.each(function(f){
7303            f.clearInvalid();
7304         });
7305         
7306         
7307         
7308         return this;
7309     },
7310
7311     /**
7312      * Resets this form.
7313      * @return {BasicForm} this
7314      */
7315     reset : function(){
7316         var items = this.getItems();
7317         items.each(function(f){
7318             f.reset();
7319         });
7320         
7321         Roo.each(this.childForms || [], function (f) {
7322             f.reset();
7323         });
7324        
7325         
7326         return this;
7327     },
7328     getItems : function()
7329     {
7330         var r=new Roo.util.MixedCollection(false, function(o){
7331             return o.id || (o.id = Roo.id());
7332         });
7333         var iter = function(el) {
7334             if (el.inputEl) {
7335                 r.add(el);
7336             }
7337             if (!el.items) {
7338                 return;
7339             }
7340             Roo.each(el.items,function(e) {
7341                 iter(e);
7342             });
7343             
7344             
7345         };
7346         
7347         iter(this);
7348         return r;
7349         
7350         
7351         
7352         
7353     }
7354     
7355 });
7356
7357  
7358 /*
7359  * Based on:
7360  * Ext JS Library 1.1.1
7361  * Copyright(c) 2006-2007, Ext JS, LLC.
7362  *
7363  * Originally Released Under LGPL - original licence link has changed is not relivant.
7364  *
7365  * Fork - LGPL
7366  * <script type="text/javascript">
7367  */
7368 /**
7369  * @class Roo.form.VTypes
7370  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7371  * @singleton
7372  */
7373 Roo.form.VTypes = function(){
7374     // closure these in so they are only created once.
7375     var alpha = /^[a-zA-Z_]+$/;
7376     var alphanum = /^[a-zA-Z0-9_]+$/;
7377     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7378     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7379
7380     // All these messages and functions are configurable
7381     return {
7382         /**
7383          * The function used to validate email addresses
7384          * @param {String} value The email address
7385          */
7386         'email' : function(v){
7387             return email.test(v);
7388         },
7389         /**
7390          * The error text to display when the email validation function returns false
7391          * @type String
7392          */
7393         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7394         /**
7395          * The keystroke filter mask to be applied on email input
7396          * @type RegExp
7397          */
7398         'emailMask' : /[a-z0-9_\.\-@]/i,
7399
7400         /**
7401          * The function used to validate URLs
7402          * @param {String} value The URL
7403          */
7404         'url' : function(v){
7405             return url.test(v);
7406         },
7407         /**
7408          * The error text to display when the url validation function returns false
7409          * @type String
7410          */
7411         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7412         
7413         /**
7414          * The function used to validate alpha values
7415          * @param {String} value The value
7416          */
7417         'alpha' : function(v){
7418             return alpha.test(v);
7419         },
7420         /**
7421          * The error text to display when the alpha validation function returns false
7422          * @type String
7423          */
7424         'alphaText' : 'This field should only contain letters and _',
7425         /**
7426          * The keystroke filter mask to be applied on alpha input
7427          * @type RegExp
7428          */
7429         'alphaMask' : /[a-z_]/i,
7430
7431         /**
7432          * The function used to validate alphanumeric values
7433          * @param {String} value The value
7434          */
7435         'alphanum' : function(v){
7436             return alphanum.test(v);
7437         },
7438         /**
7439          * The error text to display when the alphanumeric validation function returns false
7440          * @type String
7441          */
7442         'alphanumText' : 'This field should only contain letters, numbers and _',
7443         /**
7444          * The keystroke filter mask to be applied on alphanumeric input
7445          * @type RegExp
7446          */
7447         'alphanumMask' : /[a-z0-9_]/i
7448     };
7449 }();/*
7450  * - LGPL
7451  *
7452  * Input
7453  * 
7454  */
7455
7456 /**
7457  * @class Roo.bootstrap.Input
7458  * @extends Roo.bootstrap.Component
7459  * Bootstrap Input class
7460  * @cfg {Boolean} disabled is it disabled
7461  * @cfg {String} fieldLabel - the label associated
7462  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7463  * @cfg {String} name name of the input
7464  * @cfg {string} fieldLabel - the label associated
7465  * @cfg {string}  inputType - input / file submit ...
7466  * @cfg {string} placeholder - placeholder to put in text.
7467  * @cfg {string}  before - input group add on before
7468  * @cfg {string} after - input group add on after
7469  * @cfg {string} size - (lg|sm) or leave empty..
7470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7472  * @cfg {Number} md colspan out of 12 for computer-sized screens
7473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7474  * @cfg {string} value default value of the input
7475  * @cfg {Number} labelWidth set the width of label (0-12)
7476  * @cfg {String} labelAlign (top|left)
7477  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7478  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7479
7480  * @cfg {String} align (left|center|right) Default left
7481  * @cfg {Boolean} forceFeedback (true|false) Default false
7482  * 
7483  * 
7484  * 
7485  * 
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         var feedback = this.el.select('.form-control-feedback', true).first();
8173             
8174         if(feedback){
8175             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8176         }
8177
8178         if(this.disabled || this.allowBlank){
8179             return;
8180         }
8181         
8182         this.el.addClass(this.validClass);
8183         
8184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8185             
8186             var feedback = this.el.select('.form-control-feedback', true).first();
8187             
8188             if(feedback){
8189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8191             }
8192             
8193         }
8194         
8195         this.fireEvent('valid', this);
8196     },
8197     
8198      /**
8199      * Mark this field as invalid
8200      * @param {String} msg The validation message
8201      */
8202     markInvalid : function(msg){
8203         if(!this.el  || this.preventMark){ // not rendered
8204             return;
8205         }
8206         
8207         this.el.removeClass([this.invalidClass, this.validClass]);
8208         
8209         var feedback = this.el.select('.form-control-feedback', true).first();
8210             
8211         if(feedback){
8212             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8213         }
8214
8215         if(this.disabled || this.allowBlank){
8216             return;
8217         }
8218         
8219         this.el.addClass(this.invalidClass);
8220         
8221         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8222             
8223             var feedback = this.el.select('.form-control-feedback', true).first();
8224             
8225             if(feedback){
8226                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8227                 
8228                 if(this.getValue().length || this.forceFeedback){
8229                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8230                 }
8231                 
8232             }
8233             
8234         }
8235         
8236         this.fireEvent('invalid', this, msg);
8237     },
8238     // private
8239     SafariOnKeyDown : function(event)
8240     {
8241         // this is a workaround for a password hang bug on chrome/ webkit.
8242         
8243         var isSelectAll = false;
8244         
8245         if(this.inputEl().dom.selectionEnd > 0){
8246             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8247         }
8248         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8249             event.preventDefault();
8250             this.setValue('');
8251             return;
8252         }
8253         
8254         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8255             
8256             event.preventDefault();
8257             // this is very hacky as keydown always get's upper case.
8258             //
8259             var cc = String.fromCharCode(event.getCharCode());
8260             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8261             
8262         }
8263     },
8264     adjustWidth : function(tag, w){
8265         tag = tag.toLowerCase();
8266         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8267             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8268                 if(tag == 'input'){
8269                     return w + 2;
8270                 }
8271                 if(tag == 'textarea'){
8272                     return w-2;
8273                 }
8274             }else if(Roo.isOpera){
8275                 if(tag == 'input'){
8276                     return w + 2;
8277                 }
8278                 if(tag == 'textarea'){
8279                     return w-2;
8280                 }
8281             }
8282         }
8283         return w;
8284     }
8285     
8286 });
8287
8288  
8289 /*
8290  * - LGPL
8291  *
8292  * Input
8293  * 
8294  */
8295
8296 /**
8297  * @class Roo.bootstrap.TextArea
8298  * @extends Roo.bootstrap.Input
8299  * Bootstrap TextArea class
8300  * @cfg {Number} cols Specifies the visible width of a text area
8301  * @cfg {Number} rows Specifies the visible number of lines in a text area
8302  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8303  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8304  * @cfg {string} html text
8305  * 
8306  * @constructor
8307  * Create a new TextArea
8308  * @param {Object} config The config object
8309  */
8310
8311 Roo.bootstrap.TextArea = function(config){
8312     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8313    
8314 };
8315
8316 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8317      
8318     cols : false,
8319     rows : 5,
8320     readOnly : false,
8321     warp : 'soft',
8322     resize : false,
8323     value: false,
8324     html: false,
8325     
8326     getAutoCreate : function(){
8327         
8328         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8329         
8330         var id = Roo.id();
8331         
8332         var cfg = {};
8333         
8334         var input =  {
8335             tag: 'textarea',
8336             id : id,
8337             warp : this.warp,
8338             rows : this.rows,
8339             value : this.value || '',
8340             html: this.html || '',
8341             cls : 'form-control',
8342             placeholder : this.placeholder || '' 
8343             
8344         };
8345         
8346         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8347             input.maxLength = this.maxLength;
8348         }
8349         
8350         if(this.resize){
8351             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8352         }
8353         
8354         if(this.cols){
8355             input.cols = this.cols;
8356         }
8357         
8358         if (this.readOnly) {
8359             input.readonly = true;
8360         }
8361         
8362         if (this.name) {
8363             input.name = this.name;
8364         }
8365         
8366         if (this.size) {
8367             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8368         }
8369         
8370         var settings=this;
8371         ['xs','sm','md','lg'].map(function(size){
8372             if (settings[size]) {
8373                 cfg.cls += ' col-' + size + '-' + settings[size];
8374             }
8375         });
8376         
8377         var inputblock = input;
8378         
8379         if(this.hasFeedback && !this.allowBlank){
8380             
8381             var feedback = {
8382                 tag: 'span',
8383                 cls: 'glyphicon form-control-feedback'
8384             };
8385
8386             inputblock = {
8387                 cls : 'has-feedback',
8388                 cn :  [
8389                     input,
8390                     feedback
8391                 ] 
8392             };  
8393         }
8394         
8395         
8396         if (this.before || this.after) {
8397             
8398             inputblock = {
8399                 cls : 'input-group',
8400                 cn :  [] 
8401             };
8402             if (this.before) {
8403                 inputblock.cn.push({
8404                     tag :'span',
8405                     cls : 'input-group-addon',
8406                     html : this.before
8407                 });
8408             }
8409             
8410             inputblock.cn.push(input);
8411             
8412             if(this.hasFeedback && !this.allowBlank){
8413                 inputblock.cls += ' has-feedback';
8414                 inputblock.cn.push(feedback);
8415             }
8416             
8417             if (this.after) {
8418                 inputblock.cn.push({
8419                     tag :'span',
8420                     cls : 'input-group-addon',
8421                     html : this.after
8422                 });
8423             }
8424             
8425         }
8426         
8427         if (align ==='left' && this.fieldLabel.length) {
8428                 Roo.log("left and has label");
8429                 cfg.cn = [
8430                     
8431                     {
8432                         tag: 'label',
8433                         'for' :  id,
8434                         cls : 'control-label col-sm-' + this.labelWidth,
8435                         html : this.fieldLabel
8436                         
8437                     },
8438                     {
8439                         cls : "col-sm-" + (12 - this.labelWidth), 
8440                         cn: [
8441                             inputblock
8442                         ]
8443                     }
8444                     
8445                 ];
8446         } else if ( this.fieldLabel.length) {
8447                 Roo.log(" label");
8448                  cfg.cn = [
8449                    
8450                     {
8451                         tag: 'label',
8452                         //cls : 'input-group-addon',
8453                         html : this.fieldLabel
8454                         
8455                     },
8456                     
8457                     inputblock
8458                     
8459                 ];
8460
8461         } else {
8462             
8463                    Roo.log(" no label && no align");
8464                 cfg.cn = [
8465                     
8466                         inputblock
8467                     
8468                 ];
8469                 
8470                 
8471         }
8472         
8473         if (this.disabled) {
8474             input.disabled=true;
8475         }
8476         
8477         return cfg;
8478         
8479     },
8480     /**
8481      * return the real textarea element.
8482      */
8483     inputEl: function ()
8484     {
8485         return this.el.select('textarea.form-control',true).first();
8486     }
8487 });
8488
8489  
8490 /*
8491  * - LGPL
8492  *
8493  * trigger field - base class for combo..
8494  * 
8495  */
8496  
8497 /**
8498  * @class Roo.bootstrap.TriggerField
8499  * @extends Roo.bootstrap.Input
8500  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8501  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8502  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8503  * for which you can provide a custom implementation.  For example:
8504  * <pre><code>
8505 var trigger = new Roo.bootstrap.TriggerField();
8506 trigger.onTriggerClick = myTriggerFn;
8507 trigger.applyTo('my-field');
8508 </code></pre>
8509  *
8510  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8511  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8512  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8513  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8514  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8515
8516  * @constructor
8517  * Create a new TriggerField.
8518  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8519  * to the base TextField)
8520  */
8521 Roo.bootstrap.TriggerField = function(config){
8522     this.mimicing = false;
8523     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8524 };
8525
8526 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8527     /**
8528      * @cfg {String} triggerClass A CSS class to apply to the trigger
8529      */
8530      /**
8531      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8532      */
8533     hideTrigger:false,
8534
8535     /**
8536      * @cfg {Boolean} removable (true|false) special filter default false
8537      */
8538     removable : false,
8539     
8540     /** @cfg {Boolean} grow @hide */
8541     /** @cfg {Number} growMin @hide */
8542     /** @cfg {Number} growMax @hide */
8543
8544     /**
8545      * @hide 
8546      * @method
8547      */
8548     autoSize: Roo.emptyFn,
8549     // private
8550     monitorTab : true,
8551     // private
8552     deferHeight : true,
8553
8554     
8555     actionMode : 'wrap',
8556     
8557     caret : false,
8558     
8559     
8560     getAutoCreate : function(){
8561        
8562         var align = this.labelAlign || this.parentLabelAlign();
8563         
8564         var id = Roo.id();
8565         
8566         var cfg = {
8567             cls: 'form-group' //input-group
8568         };
8569         
8570         
8571         var input =  {
8572             tag: 'input',
8573             id : id,
8574             type : this.inputType,
8575             cls : 'form-control',
8576             autocomplete: 'new-password',
8577             placeholder : this.placeholder || '' 
8578             
8579         };
8580         if (this.name) {
8581             input.name = this.name;
8582         }
8583         if (this.size) {
8584             input.cls += ' input-' + this.size;
8585         }
8586         
8587         if (this.disabled) {
8588             input.disabled=true;
8589         }
8590         
8591         var inputblock = input;
8592         
8593         if(this.hasFeedback && !this.allowBlank){
8594             
8595             var feedback = {
8596                 tag: 'span',
8597                 cls: 'glyphicon form-control-feedback'
8598             };
8599             
8600             if(this.removable && !this.editable && !this.tickable){
8601                 inputblock = {
8602                     cls : 'has-feedback',
8603                     cn :  [
8604                         inputblock,
8605                         {
8606                             tag: 'button',
8607                             html : 'x',
8608                             cls : 'roo-combo-removable-btn close'
8609                         },
8610                         feedback
8611                     ] 
8612                 };
8613             } else {
8614                 inputblock = {
8615                     cls : 'has-feedback',
8616                     cn :  [
8617                         inputblock,
8618                         feedback
8619                     ] 
8620                 };
8621             }
8622
8623         } else {
8624             if(this.removable && !this.editable && !this.tickable){
8625                 inputblock = {
8626                     cls : 'roo-removable',
8627                     cn :  [
8628                         inputblock,
8629                         {
8630                             tag: 'button',
8631                             html : 'x',
8632                             cls : 'roo-combo-removable-btn close'
8633                         }
8634                     ] 
8635                 };
8636             }
8637         }
8638         
8639         if (this.before || this.after) {
8640             
8641             inputblock = {
8642                 cls : 'input-group',
8643                 cn :  [] 
8644             };
8645             if (this.before) {
8646                 inputblock.cn.push({
8647                     tag :'span',
8648                     cls : 'input-group-addon',
8649                     html : this.before
8650                 });
8651             }
8652             
8653             inputblock.cn.push(input);
8654             
8655             if(this.hasFeedback && !this.allowBlank){
8656                 inputblock.cls += ' has-feedback';
8657                 inputblock.cn.push(feedback);
8658             }
8659             
8660             if (this.after) {
8661                 inputblock.cn.push({
8662                     tag :'span',
8663                     cls : 'input-group-addon',
8664                     html : this.after
8665                 });
8666             }
8667             
8668         };
8669         
8670         var box = {
8671             tag: 'div',
8672             cn: [
8673                 {
8674                     tag: 'input',
8675                     type : 'hidden',
8676                     cls: 'form-hidden-field'
8677                 },
8678                 inputblock
8679             ]
8680             
8681         };
8682         
8683         if(this.multiple){
8684             Roo.log('multiple');
8685             
8686             box = {
8687                 tag: 'div',
8688                 cn: [
8689                     {
8690                         tag: 'input',
8691                         type : 'hidden',
8692                         cls: 'form-hidden-field'
8693                     },
8694                     {
8695                         tag: 'ul',
8696                         cls: 'select2-choices',
8697                         cn:[
8698                             {
8699                                 tag: 'li',
8700                                 cls: 'select2-search-field',
8701                                 cn: [
8702
8703                                     inputblock
8704                                 ]
8705                             }
8706                         ]
8707                     }
8708                 ]
8709             }
8710         };
8711         
8712         var combobox = {
8713             cls: 'select2-container input-group',
8714             cn: [
8715                 box
8716 //                {
8717 //                    tag: 'ul',
8718 //                    cls: 'typeahead typeahead-long dropdown-menu',
8719 //                    style: 'display:none'
8720 //                }
8721             ]
8722         };
8723         
8724         if(!this.multiple && this.showToggleBtn){
8725             
8726             var caret = {
8727                         tag: 'span',
8728                         cls: 'caret'
8729              };
8730             if (this.caret != false) {
8731                 caret = {
8732                      tag: 'i',
8733                      cls: 'fa fa-' + this.caret
8734                 };
8735                 
8736             }
8737             
8738             combobox.cn.push({
8739                 tag :'span',
8740                 cls : 'input-group-addon btn dropdown-toggle',
8741                 cn : [
8742                     caret,
8743                     {
8744                         tag: 'span',
8745                         cls: 'combobox-clear',
8746                         cn  : [
8747                             {
8748                                 tag : 'i',
8749                                 cls: 'icon-remove'
8750                             }
8751                         ]
8752                     }
8753                 ]
8754
8755             })
8756         }
8757         
8758         if(this.multiple){
8759             combobox.cls += ' select2-container-multi';
8760         }
8761         
8762         if (align ==='left' && this.fieldLabel.length) {
8763             
8764                 Roo.log("left and has label");
8765                 cfg.cn = [
8766                     
8767                     {
8768                         tag: 'label',
8769                         'for' :  id,
8770                         cls : 'control-label col-sm-' + this.labelWidth,
8771                         html : this.fieldLabel
8772                         
8773                     },
8774                     {
8775                         cls : "col-sm-" + (12 - this.labelWidth), 
8776                         cn: [
8777                             combobox
8778                         ]
8779                     }
8780                     
8781                 ];
8782         } else if ( this.fieldLabel.length) {
8783                 Roo.log(" label");
8784                  cfg.cn = [
8785                    
8786                     {
8787                         tag: 'label',
8788                         //cls : 'input-group-addon',
8789                         html : this.fieldLabel
8790                         
8791                     },
8792                     
8793                     combobox
8794                     
8795                 ];
8796
8797         } else {
8798             
8799                 Roo.log(" no label && no align");
8800                 cfg = combobox
8801                      
8802                 
8803         }
8804          
8805         var settings=this;
8806         ['xs','sm','md','lg'].map(function(size){
8807             if (settings[size]) {
8808                 cfg.cls += ' col-' + size + '-' + settings[size];
8809             }
8810         });
8811         Roo.log(cfg);
8812         return cfg;
8813         
8814     },
8815     
8816     
8817     
8818     // private
8819     onResize : function(w, h){
8820 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8821 //        if(typeof w == 'number'){
8822 //            var x = w - this.trigger.getWidth();
8823 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8824 //            this.trigger.setStyle('left', x+'px');
8825 //        }
8826     },
8827
8828     // private
8829     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8830
8831     // private
8832     getResizeEl : function(){
8833         return this.inputEl();
8834     },
8835
8836     // private
8837     getPositionEl : function(){
8838         return this.inputEl();
8839     },
8840
8841     // private
8842     alignErrorIcon : function(){
8843         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8844     },
8845
8846     // private
8847     initEvents : function(){
8848         
8849         this.createList();
8850         
8851         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8852         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8853         if(!this.multiple && this.showToggleBtn){
8854             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8855             if(this.hideTrigger){
8856                 this.trigger.setDisplayed(false);
8857             }
8858             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8859         }
8860         
8861         if(this.multiple){
8862             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8863         }
8864         
8865         if(this.removable && !this.editable && !this.tickable){
8866             var close = this.closeTriggerEl();
8867             
8868             if(close){
8869                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8870                 close.on('click', this.removeBtnClick, this, close);
8871             }
8872         }
8873         
8874         //this.trigger.addClassOnOver('x-form-trigger-over');
8875         //this.trigger.addClassOnClick('x-form-trigger-click');
8876         
8877         //if(!this.width){
8878         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8879         //}
8880     },
8881     
8882     closeTriggerEl : function()
8883     {
8884         var close = this.el.select('.roo-combo-removable-btn', true).first();
8885         return close ? close : false;
8886     },
8887     
8888     removeBtnClick : function(e, h, el)
8889     {
8890         e.preventDefault();
8891         
8892         if(this.fireEvent("remove", this) !== false){
8893             this.reset();
8894         }
8895     },
8896     
8897     createList : function()
8898     {
8899         this.list = Roo.get(document.body).createChild({
8900             tag: 'ul',
8901             cls: 'typeahead typeahead-long dropdown-menu',
8902             style: 'display:none'
8903         });
8904         
8905         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8906         
8907     },
8908
8909     // private
8910     initTrigger : function(){
8911        
8912     },
8913
8914     // private
8915     onDestroy : function(){
8916         if(this.trigger){
8917             this.trigger.removeAllListeners();
8918           //  this.trigger.remove();
8919         }
8920         //if(this.wrap){
8921         //    this.wrap.remove();
8922         //}
8923         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8924     },
8925
8926     // private
8927     onFocus : function(){
8928         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8929         /*
8930         if(!this.mimicing){
8931             this.wrap.addClass('x-trigger-wrap-focus');
8932             this.mimicing = true;
8933             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8934             if(this.monitorTab){
8935                 this.el.on("keydown", this.checkTab, this);
8936             }
8937         }
8938         */
8939     },
8940
8941     // private
8942     checkTab : function(e){
8943         if(e.getKey() == e.TAB){
8944             this.triggerBlur();
8945         }
8946     },
8947
8948     // private
8949     onBlur : function(){
8950         // do nothing
8951     },
8952
8953     // private
8954     mimicBlur : function(e, t){
8955         /*
8956         if(!this.wrap.contains(t) && this.validateBlur()){
8957             this.triggerBlur();
8958         }
8959         */
8960     },
8961
8962     // private
8963     triggerBlur : function(){
8964         this.mimicing = false;
8965         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8966         if(this.monitorTab){
8967             this.el.un("keydown", this.checkTab, this);
8968         }
8969         //this.wrap.removeClass('x-trigger-wrap-focus');
8970         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8971     },
8972
8973     // private
8974     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8975     validateBlur : function(e, t){
8976         return true;
8977     },
8978
8979     // private
8980     onDisable : function(){
8981         this.inputEl().dom.disabled = true;
8982         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8983         //if(this.wrap){
8984         //    this.wrap.addClass('x-item-disabled');
8985         //}
8986     },
8987
8988     // private
8989     onEnable : function(){
8990         this.inputEl().dom.disabled = false;
8991         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8992         //if(this.wrap){
8993         //    this.el.removeClass('x-item-disabled');
8994         //}
8995     },
8996
8997     // private
8998     onShow : function(){
8999         var ae = this.getActionEl();
9000         
9001         if(ae){
9002             ae.dom.style.display = '';
9003             ae.dom.style.visibility = 'visible';
9004         }
9005     },
9006
9007     // private
9008     
9009     onHide : function(){
9010         var ae = this.getActionEl();
9011         ae.dom.style.display = 'none';
9012     },
9013
9014     /**
9015      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9016      * by an implementing function.
9017      * @method
9018      * @param {EventObject} e
9019      */
9020     onTriggerClick : Roo.emptyFn
9021 });
9022  /*
9023  * Based on:
9024  * Ext JS Library 1.1.1
9025  * Copyright(c) 2006-2007, Ext JS, LLC.
9026  *
9027  * Originally Released Under LGPL - original licence link has changed is not relivant.
9028  *
9029  * Fork - LGPL
9030  * <script type="text/javascript">
9031  */
9032
9033
9034 /**
9035  * @class Roo.data.SortTypes
9036  * @singleton
9037  * Defines the default sorting (casting?) comparison functions used when sorting data.
9038  */
9039 Roo.data.SortTypes = {
9040     /**
9041      * Default sort that does nothing
9042      * @param {Mixed} s The value being converted
9043      * @return {Mixed} The comparison value
9044      */
9045     none : function(s){
9046         return s;
9047     },
9048     
9049     /**
9050      * The regular expression used to strip tags
9051      * @type {RegExp}
9052      * @property
9053      */
9054     stripTagsRE : /<\/?[^>]+>/gi,
9055     
9056     /**
9057      * Strips all HTML tags to sort on text only
9058      * @param {Mixed} s The value being converted
9059      * @return {String} The comparison value
9060      */
9061     asText : function(s){
9062         return String(s).replace(this.stripTagsRE, "");
9063     },
9064     
9065     /**
9066      * Strips all HTML tags to sort on text only - Case insensitive
9067      * @param {Mixed} s The value being converted
9068      * @return {String} The comparison value
9069      */
9070     asUCText : function(s){
9071         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9072     },
9073     
9074     /**
9075      * Case insensitive string
9076      * @param {Mixed} s The value being converted
9077      * @return {String} The comparison value
9078      */
9079     asUCString : function(s) {
9080         return String(s).toUpperCase();
9081     },
9082     
9083     /**
9084      * Date sorting
9085      * @param {Mixed} s The value being converted
9086      * @return {Number} The comparison value
9087      */
9088     asDate : function(s) {
9089         if(!s){
9090             return 0;
9091         }
9092         if(s instanceof Date){
9093             return s.getTime();
9094         }
9095         return Date.parse(String(s));
9096     },
9097     
9098     /**
9099      * Float sorting
9100      * @param {Mixed} s The value being converted
9101      * @return {Float} The comparison value
9102      */
9103     asFloat : function(s) {
9104         var val = parseFloat(String(s).replace(/,/g, ""));
9105         if(isNaN(val)) val = 0;
9106         return val;
9107     },
9108     
9109     /**
9110      * Integer sorting
9111      * @param {Mixed} s The value being converted
9112      * @return {Number} The comparison value
9113      */
9114     asInt : function(s) {
9115         var val = parseInt(String(s).replace(/,/g, ""));
9116         if(isNaN(val)) val = 0;
9117         return val;
9118     }
9119 };/*
9120  * Based on:
9121  * Ext JS Library 1.1.1
9122  * Copyright(c) 2006-2007, Ext JS, LLC.
9123  *
9124  * Originally Released Under LGPL - original licence link has changed is not relivant.
9125  *
9126  * Fork - LGPL
9127  * <script type="text/javascript">
9128  */
9129
9130 /**
9131 * @class Roo.data.Record
9132  * Instances of this class encapsulate both record <em>definition</em> information, and record
9133  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9134  * to access Records cached in an {@link Roo.data.Store} object.<br>
9135  * <p>
9136  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9137  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9138  * objects.<br>
9139  * <p>
9140  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9141  * @constructor
9142  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9143  * {@link #create}. The parameters are the same.
9144  * @param {Array} data An associative Array of data values keyed by the field name.
9145  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9146  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9147  * not specified an integer id is generated.
9148  */
9149 Roo.data.Record = function(data, id){
9150     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9151     this.data = data;
9152 };
9153
9154 /**
9155  * Generate a constructor for a specific record layout.
9156  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9157  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9158  * Each field definition object may contain the following properties: <ul>
9159  * <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,
9160  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9161  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9162  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9163  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9164  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9165  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9166  * this may be omitted.</p></li>
9167  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9168  * <ul><li>auto (Default, implies no conversion)</li>
9169  * <li>string</li>
9170  * <li>int</li>
9171  * <li>float</li>
9172  * <li>boolean</li>
9173  * <li>date</li></ul></p></li>
9174  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9175  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9176  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9177  * by the Reader into an object that will be stored in the Record. It is passed the
9178  * following parameters:<ul>
9179  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9180  * </ul></p></li>
9181  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9182  * </ul>
9183  * <br>usage:<br><pre><code>
9184 var TopicRecord = Roo.data.Record.create(
9185     {name: 'title', mapping: 'topic_title'},
9186     {name: 'author', mapping: 'username'},
9187     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9188     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9189     {name: 'lastPoster', mapping: 'user2'},
9190     {name: 'excerpt', mapping: 'post_text'}
9191 );
9192
9193 var myNewRecord = new TopicRecord({
9194     title: 'Do my job please',
9195     author: 'noobie',
9196     totalPosts: 1,
9197     lastPost: new Date(),
9198     lastPoster: 'Animal',
9199     excerpt: 'No way dude!'
9200 });
9201 myStore.add(myNewRecord);
9202 </code></pre>
9203  * @method create
9204  * @static
9205  */
9206 Roo.data.Record.create = function(o){
9207     var f = function(){
9208         f.superclass.constructor.apply(this, arguments);
9209     };
9210     Roo.extend(f, Roo.data.Record);
9211     var p = f.prototype;
9212     p.fields = new Roo.util.MixedCollection(false, function(field){
9213         return field.name;
9214     });
9215     for(var i = 0, len = o.length; i < len; i++){
9216         p.fields.add(new Roo.data.Field(o[i]));
9217     }
9218     f.getField = function(name){
9219         return p.fields.get(name);  
9220     };
9221     return f;
9222 };
9223
9224 Roo.data.Record.AUTO_ID = 1000;
9225 Roo.data.Record.EDIT = 'edit';
9226 Roo.data.Record.REJECT = 'reject';
9227 Roo.data.Record.COMMIT = 'commit';
9228
9229 Roo.data.Record.prototype = {
9230     /**
9231      * Readonly flag - true if this record has been modified.
9232      * @type Boolean
9233      */
9234     dirty : false,
9235     editing : false,
9236     error: null,
9237     modified: null,
9238
9239     // private
9240     join : function(store){
9241         this.store = store;
9242     },
9243
9244     /**
9245      * Set the named field to the specified value.
9246      * @param {String} name The name of the field to set.
9247      * @param {Object} value The value to set the field to.
9248      */
9249     set : function(name, value){
9250         if(this.data[name] == value){
9251             return;
9252         }
9253         this.dirty = true;
9254         if(!this.modified){
9255             this.modified = {};
9256         }
9257         if(typeof this.modified[name] == 'undefined'){
9258             this.modified[name] = this.data[name];
9259         }
9260         this.data[name] = value;
9261         if(!this.editing && this.store){
9262             this.store.afterEdit(this);
9263         }       
9264     },
9265
9266     /**
9267      * Get the value of the named field.
9268      * @param {String} name The name of the field to get the value of.
9269      * @return {Object} The value of the field.
9270      */
9271     get : function(name){
9272         return this.data[name]; 
9273     },
9274
9275     // private
9276     beginEdit : function(){
9277         this.editing = true;
9278         this.modified = {}; 
9279     },
9280
9281     // private
9282     cancelEdit : function(){
9283         this.editing = false;
9284         delete this.modified;
9285     },
9286
9287     // private
9288     endEdit : function(){
9289         this.editing = false;
9290         if(this.dirty && this.store){
9291             this.store.afterEdit(this);
9292         }
9293     },
9294
9295     /**
9296      * Usually called by the {@link Roo.data.Store} which owns the Record.
9297      * Rejects all changes made to the Record since either creation, or the last commit operation.
9298      * Modified fields are reverted to their original values.
9299      * <p>
9300      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9301      * of reject operations.
9302      */
9303     reject : function(){
9304         var m = this.modified;
9305         for(var n in m){
9306             if(typeof m[n] != "function"){
9307                 this.data[n] = m[n];
9308             }
9309         }
9310         this.dirty = false;
9311         delete this.modified;
9312         this.editing = false;
9313         if(this.store){
9314             this.store.afterReject(this);
9315         }
9316     },
9317
9318     /**
9319      * Usually called by the {@link Roo.data.Store} which owns the Record.
9320      * Commits all changes made to the Record since either creation, or the last commit operation.
9321      * <p>
9322      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9323      * of commit operations.
9324      */
9325     commit : function(){
9326         this.dirty = false;
9327         delete this.modified;
9328         this.editing = false;
9329         if(this.store){
9330             this.store.afterCommit(this);
9331         }
9332     },
9333
9334     // private
9335     hasError : function(){
9336         return this.error != null;
9337     },
9338
9339     // private
9340     clearError : function(){
9341         this.error = null;
9342     },
9343
9344     /**
9345      * Creates a copy of this record.
9346      * @param {String} id (optional) A new record id if you don't want to use this record's id
9347      * @return {Record}
9348      */
9349     copy : function(newId) {
9350         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9351     }
9352 };/*
9353  * Based on:
9354  * Ext JS Library 1.1.1
9355  * Copyright(c) 2006-2007, Ext JS, LLC.
9356  *
9357  * Originally Released Under LGPL - original licence link has changed is not relivant.
9358  *
9359  * Fork - LGPL
9360  * <script type="text/javascript">
9361  */
9362
9363
9364
9365 /**
9366  * @class Roo.data.Store
9367  * @extends Roo.util.Observable
9368  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9369  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9370  * <p>
9371  * 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
9372  * has no knowledge of the format of the data returned by the Proxy.<br>
9373  * <p>
9374  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9375  * instances from the data object. These records are cached and made available through accessor functions.
9376  * @constructor
9377  * Creates a new Store.
9378  * @param {Object} config A config object containing the objects needed for the Store to access data,
9379  * and read the data into Records.
9380  */
9381 Roo.data.Store = function(config){
9382     this.data = new Roo.util.MixedCollection(false);
9383     this.data.getKey = function(o){
9384         return o.id;
9385     };
9386     this.baseParams = {};
9387     // private
9388     this.paramNames = {
9389         "start" : "start",
9390         "limit" : "limit",
9391         "sort" : "sort",
9392         "dir" : "dir",
9393         "multisort" : "_multisort"
9394     };
9395
9396     if(config && config.data){
9397         this.inlineData = config.data;
9398         delete config.data;
9399     }
9400
9401     Roo.apply(this, config);
9402     
9403     if(this.reader){ // reader passed
9404         this.reader = Roo.factory(this.reader, Roo.data);
9405         this.reader.xmodule = this.xmodule || false;
9406         if(!this.recordType){
9407             this.recordType = this.reader.recordType;
9408         }
9409         if(this.reader.onMetaChange){
9410             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9411         }
9412     }
9413
9414     if(this.recordType){
9415         this.fields = this.recordType.prototype.fields;
9416     }
9417     this.modified = [];
9418
9419     this.addEvents({
9420         /**
9421          * @event datachanged
9422          * Fires when the data cache has changed, and a widget which is using this Store
9423          * as a Record cache should refresh its view.
9424          * @param {Store} this
9425          */
9426         datachanged : true,
9427         /**
9428          * @event metachange
9429          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9430          * @param {Store} this
9431          * @param {Object} meta The JSON metadata
9432          */
9433         metachange : true,
9434         /**
9435          * @event add
9436          * Fires when Records have been added to the Store
9437          * @param {Store} this
9438          * @param {Roo.data.Record[]} records The array of Records added
9439          * @param {Number} index The index at which the record(s) were added
9440          */
9441         add : true,
9442         /**
9443          * @event remove
9444          * Fires when a Record has been removed from the Store
9445          * @param {Store} this
9446          * @param {Roo.data.Record} record The Record that was removed
9447          * @param {Number} index The index at which the record was removed
9448          */
9449         remove : true,
9450         /**
9451          * @event update
9452          * Fires when a Record has been updated
9453          * @param {Store} this
9454          * @param {Roo.data.Record} record The Record that was updated
9455          * @param {String} operation The update operation being performed.  Value may be one of:
9456          * <pre><code>
9457  Roo.data.Record.EDIT
9458  Roo.data.Record.REJECT
9459  Roo.data.Record.COMMIT
9460          * </code></pre>
9461          */
9462         update : true,
9463         /**
9464          * @event clear
9465          * Fires when the data cache has been cleared.
9466          * @param {Store} this
9467          */
9468         clear : true,
9469         /**
9470          * @event beforeload
9471          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9472          * the load action will be canceled.
9473          * @param {Store} this
9474          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9475          */
9476         beforeload : true,
9477         /**
9478          * @event beforeloadadd
9479          * Fires after a new set of Records has been loaded.
9480          * @param {Store} this
9481          * @param {Roo.data.Record[]} records The Records that were loaded
9482          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9483          */
9484         beforeloadadd : true,
9485         /**
9486          * @event load
9487          * Fires after a new set of Records has been loaded, before they are added to the store.
9488          * @param {Store} this
9489          * @param {Roo.data.Record[]} records The Records that were loaded
9490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9491          * @params {Object} return from reader
9492          */
9493         load : true,
9494         /**
9495          * @event loadexception
9496          * Fires if an exception occurs in the Proxy during loading.
9497          * Called with the signature of the Proxy's "loadexception" event.
9498          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9499          * 
9500          * @param {Proxy} 
9501          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9502          * @param {Object} load options 
9503          * @param {Object} jsonData from your request (normally this contains the Exception)
9504          */
9505         loadexception : true
9506     });
9507     
9508     if(this.proxy){
9509         this.proxy = Roo.factory(this.proxy, Roo.data);
9510         this.proxy.xmodule = this.xmodule || false;
9511         this.relayEvents(this.proxy,  ["loadexception"]);
9512     }
9513     this.sortToggle = {};
9514     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9515
9516     Roo.data.Store.superclass.constructor.call(this);
9517
9518     if(this.inlineData){
9519         this.loadData(this.inlineData);
9520         delete this.inlineData;
9521     }
9522 };
9523
9524 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9525      /**
9526     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9527     * without a remote query - used by combo/forms at present.
9528     */
9529     
9530     /**
9531     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9532     */
9533     /**
9534     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9535     */
9536     /**
9537     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9538     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9539     */
9540     /**
9541     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9542     * on any HTTP request
9543     */
9544     /**
9545     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9546     */
9547     /**
9548     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9549     */
9550     multiSort: false,
9551     /**
9552     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9553     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9554     */
9555     remoteSort : false,
9556
9557     /**
9558     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9559      * loaded or when a record is removed. (defaults to false).
9560     */
9561     pruneModifiedRecords : false,
9562
9563     // private
9564     lastOptions : null,
9565
9566     /**
9567      * Add Records to the Store and fires the add event.
9568      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9569      */
9570     add : function(records){
9571         records = [].concat(records);
9572         for(var i = 0, len = records.length; i < len; i++){
9573             records[i].join(this);
9574         }
9575         var index = this.data.length;
9576         this.data.addAll(records);
9577         this.fireEvent("add", this, records, index);
9578     },
9579
9580     /**
9581      * Remove a Record from the Store and fires the remove event.
9582      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9583      */
9584     remove : function(record){
9585         var index = this.data.indexOf(record);
9586         this.data.removeAt(index);
9587         if(this.pruneModifiedRecords){
9588             this.modified.remove(record);
9589         }
9590         this.fireEvent("remove", this, record, index);
9591     },
9592
9593     /**
9594      * Remove all Records from the Store and fires the clear event.
9595      */
9596     removeAll : function(){
9597         this.data.clear();
9598         if(this.pruneModifiedRecords){
9599             this.modified = [];
9600         }
9601         this.fireEvent("clear", this);
9602     },
9603
9604     /**
9605      * Inserts Records to the Store at the given index and fires the add event.
9606      * @param {Number} index The start index at which to insert the passed Records.
9607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9608      */
9609     insert : function(index, records){
9610         records = [].concat(records);
9611         for(var i = 0, len = records.length; i < len; i++){
9612             this.data.insert(index, records[i]);
9613             records[i].join(this);
9614         }
9615         this.fireEvent("add", this, records, index);
9616     },
9617
9618     /**
9619      * Get the index within the cache of the passed Record.
9620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9621      * @return {Number} The index of the passed Record. Returns -1 if not found.
9622      */
9623     indexOf : function(record){
9624         return this.data.indexOf(record);
9625     },
9626
9627     /**
9628      * Get the index within the cache of the Record with the passed id.
9629      * @param {String} id The id of the Record to find.
9630      * @return {Number} The index of the Record. Returns -1 if not found.
9631      */
9632     indexOfId : function(id){
9633         return this.data.indexOfKey(id);
9634     },
9635
9636     /**
9637      * Get the Record with the specified id.
9638      * @param {String} id The id of the Record to find.
9639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9640      */
9641     getById : function(id){
9642         return this.data.key(id);
9643     },
9644
9645     /**
9646      * Get the Record at the specified index.
9647      * @param {Number} index The index of the Record to find.
9648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9649      */
9650     getAt : function(index){
9651         return this.data.itemAt(index);
9652     },
9653
9654     /**
9655      * Returns a range of Records between specified indices.
9656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9658      * @return {Roo.data.Record[]} An array of Records
9659      */
9660     getRange : function(start, end){
9661         return this.data.getRange(start, end);
9662     },
9663
9664     // private
9665     storeOptions : function(o){
9666         o = Roo.apply({}, o);
9667         delete o.callback;
9668         delete o.scope;
9669         this.lastOptions = o;
9670     },
9671
9672     /**
9673      * Loads the Record cache from the configured Proxy using the configured Reader.
9674      * <p>
9675      * If using remote paging, then the first load call must specify the <em>start</em>
9676      * and <em>limit</em> properties in the options.params property to establish the initial
9677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9678      * <p>
9679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9680      * and this call will return before the new data has been loaded. Perform any post-processing
9681      * in a callback function, or in a "load" event handler.</strong>
9682      * <p>
9683      * @param {Object} options An object containing properties which control loading options:<ul>
9684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9686      * passed the following arguments:<ul>
9687      * <li>r : Roo.data.Record[]</li>
9688      * <li>options: Options object from the load call</li>
9689      * <li>success: Boolean success indicator</li></ul></li>
9690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9692      * </ul>
9693      */
9694     load : function(options){
9695         options = options || {};
9696         if(this.fireEvent("beforeload", this, options) !== false){
9697             this.storeOptions(options);
9698             var p = Roo.apply(options.params || {}, this.baseParams);
9699             // if meta was not loaded from remote source.. try requesting it.
9700             if (!this.reader.metaFromRemote) {
9701                 p._requestMeta = 1;
9702             }
9703             if(this.sortInfo && this.remoteSort){
9704                 var pn = this.paramNames;
9705                 p[pn["sort"]] = this.sortInfo.field;
9706                 p[pn["dir"]] = this.sortInfo.direction;
9707             }
9708             if (this.multiSort) {
9709                 var pn = this.paramNames;
9710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9711             }
9712             
9713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9714         }
9715     },
9716
9717     /**
9718      * Reloads the Record cache from the configured Proxy using the configured Reader and
9719      * the options from the last load operation performed.
9720      * @param {Object} options (optional) An object containing properties which may override the options
9721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9722      * the most recently used options are reused).
9723      */
9724     reload : function(options){
9725         this.load(Roo.applyIf(options||{}, this.lastOptions));
9726     },
9727
9728     // private
9729     // Called as a callback by the Reader during a load operation.
9730     loadRecords : function(o, options, success){
9731         if(!o || success === false){
9732             if(success !== false){
9733                 this.fireEvent("load", this, [], options, o);
9734             }
9735             if(options.callback){
9736                 options.callback.call(options.scope || this, [], options, false);
9737             }
9738             return;
9739         }
9740         // if data returned failure - throw an exception.
9741         if (o.success === false) {
9742             // show a message if no listener is registered.
9743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9745             }
9746             // loadmask wil be hooked into this..
9747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9748             return;
9749         }
9750         var r = o.records, t = o.totalRecords || r.length;
9751         
9752         this.fireEvent("beforeloadadd", this, r, options, o);
9753         
9754         if(!options || options.add !== true){
9755             if(this.pruneModifiedRecords){
9756                 this.modified = [];
9757             }
9758             for(var i = 0, len = r.length; i < len; i++){
9759                 r[i].join(this);
9760             }
9761             if(this.snapshot){
9762                 this.data = this.snapshot;
9763                 delete this.snapshot;
9764             }
9765             this.data.clear();
9766             this.data.addAll(r);
9767             this.totalLength = t;
9768             this.applySort();
9769             this.fireEvent("datachanged", this);
9770         }else{
9771             this.totalLength = Math.max(t, this.data.length+r.length);
9772             this.add(r);
9773         }
9774         this.fireEvent("load", this, r, options, o);
9775         if(options.callback){
9776             options.callback.call(options.scope || this, r, options, true);
9777         }
9778     },
9779
9780
9781     /**
9782      * Loads data from a passed data block. A Reader which understands the format of the data
9783      * must have been configured in the constructor.
9784      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9785      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9786      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9787      */
9788     loadData : function(o, append){
9789         var r = this.reader.readRecords(o);
9790         this.loadRecords(r, {add: append}, true);
9791     },
9792
9793     /**
9794      * Gets the number of cached records.
9795      * <p>
9796      * <em>If using paging, this may not be the total size of the dataset. If the data object
9797      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9798      * the data set size</em>
9799      */
9800     getCount : function(){
9801         return this.data.length || 0;
9802     },
9803
9804     /**
9805      * Gets the total number of records in the dataset as returned by the server.
9806      * <p>
9807      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9808      * the dataset size</em>
9809      */
9810     getTotalCount : function(){
9811         return this.totalLength || 0;
9812     },
9813
9814     /**
9815      * Returns the sort state of the Store as an object with two properties:
9816      * <pre><code>
9817  field {String} The name of the field by which the Records are sorted
9818  direction {String} The sort order, "ASC" or "DESC"
9819      * </code></pre>
9820      */
9821     getSortState : function(){
9822         return this.sortInfo;
9823     },
9824
9825     // private
9826     applySort : function(){
9827         if(this.sortInfo && !this.remoteSort){
9828             var s = this.sortInfo, f = s.field;
9829             var st = this.fields.get(f).sortType;
9830             var fn = function(r1, r2){
9831                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9832                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9833             };
9834             this.data.sort(s.direction, fn);
9835             if(this.snapshot && this.snapshot != this.data){
9836                 this.snapshot.sort(s.direction, fn);
9837             }
9838         }
9839     },
9840
9841     /**
9842      * Sets the default sort column and order to be used by the next load operation.
9843      * @param {String} fieldName The name of the field to sort by.
9844      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9845      */
9846     setDefaultSort : function(field, dir){
9847         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9848     },
9849
9850     /**
9851      * Sort the Records.
9852      * If remote sorting is used, the sort is performed on the server, and the cache is
9853      * reloaded. If local sorting is used, the cache is sorted internally.
9854      * @param {String} fieldName The name of the field to sort by.
9855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9856      */
9857     sort : function(fieldName, dir){
9858         var f = this.fields.get(fieldName);
9859         if(!dir){
9860             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9861             
9862             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9863                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9864             }else{
9865                 dir = f.sortDir;
9866             }
9867         }
9868         this.sortToggle[f.name] = dir;
9869         this.sortInfo = {field: f.name, direction: dir};
9870         if(!this.remoteSort){
9871             this.applySort();
9872             this.fireEvent("datachanged", this);
9873         }else{
9874             this.load(this.lastOptions);
9875         }
9876     },
9877
9878     /**
9879      * Calls the specified function for each of the Records in the cache.
9880      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9881      * Returning <em>false</em> aborts and exits the iteration.
9882      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9883      */
9884     each : function(fn, scope){
9885         this.data.each(fn, scope);
9886     },
9887
9888     /**
9889      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9890      * (e.g., during paging).
9891      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9892      */
9893     getModifiedRecords : function(){
9894         return this.modified;
9895     },
9896
9897     // private
9898     createFilterFn : function(property, value, anyMatch){
9899         if(!value.exec){ // not a regex
9900             value = String(value);
9901             if(value.length == 0){
9902                 return false;
9903             }
9904             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9905         }
9906         return function(r){
9907             return value.test(r.data[property]);
9908         };
9909     },
9910
9911     /**
9912      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9913      * @param {String} property A field on your records
9914      * @param {Number} start The record index to start at (defaults to 0)
9915      * @param {Number} end The last record index to include (defaults to length - 1)
9916      * @return {Number} The sum
9917      */
9918     sum : function(property, start, end){
9919         var rs = this.data.items, v = 0;
9920         start = start || 0;
9921         end = (end || end === 0) ? end : rs.length-1;
9922
9923         for(var i = start; i <= end; i++){
9924             v += (rs[i].data[property] || 0);
9925         }
9926         return v;
9927     },
9928
9929     /**
9930      * Filter the records by a specified property.
9931      * @param {String} field A field on your records
9932      * @param {String/RegExp} value Either a string that the field
9933      * should start with or a RegExp to test against the field
9934      * @param {Boolean} anyMatch True to match any part not just the beginning
9935      */
9936     filter : function(property, value, anyMatch){
9937         var fn = this.createFilterFn(property, value, anyMatch);
9938         return fn ? this.filterBy(fn) : this.clearFilter();
9939     },
9940
9941     /**
9942      * Filter by a function. The specified function will be called with each
9943      * record in this data source. If the function returns true the record is included,
9944      * otherwise it is filtered.
9945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9946      * @param {Object} scope (optional) The scope of the function (defaults to this)
9947      */
9948     filterBy : function(fn, scope){
9949         this.snapshot = this.snapshot || this.data;
9950         this.data = this.queryBy(fn, scope||this);
9951         this.fireEvent("datachanged", this);
9952     },
9953
9954     /**
9955      * Query the records by a specified property.
9956      * @param {String} field A field on your records
9957      * @param {String/RegExp} value Either a string that the field
9958      * should start with or a RegExp to test against the field
9959      * @param {Boolean} anyMatch True to match any part not just the beginning
9960      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9961      */
9962     query : function(property, value, anyMatch){
9963         var fn = this.createFilterFn(property, value, anyMatch);
9964         return fn ? this.queryBy(fn) : this.data.clone();
9965     },
9966
9967     /**
9968      * Query by a function. The specified function will be called with each
9969      * record in this data source. If the function returns true the record is included
9970      * in the results.
9971      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9972      * @param {Object} scope (optional) The scope of the function (defaults to this)
9973       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9974      **/
9975     queryBy : function(fn, scope){
9976         var data = this.snapshot || this.data;
9977         return data.filterBy(fn, scope||this);
9978     },
9979
9980     /**
9981      * Collects unique values for a particular dataIndex from this store.
9982      * @param {String} dataIndex The property to collect
9983      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9984      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9985      * @return {Array} An array of the unique values
9986      **/
9987     collect : function(dataIndex, allowNull, bypassFilter){
9988         var d = (bypassFilter === true && this.snapshot) ?
9989                 this.snapshot.items : this.data.items;
9990         var v, sv, r = [], l = {};
9991         for(var i = 0, len = d.length; i < len; i++){
9992             v = d[i].data[dataIndex];
9993             sv = String(v);
9994             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9995                 l[sv] = true;
9996                 r[r.length] = v;
9997             }
9998         }
9999         return r;
10000     },
10001
10002     /**
10003      * Revert to a view of the Record cache with no filtering applied.
10004      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10005      */
10006     clearFilter : function(suppressEvent){
10007         if(this.snapshot && this.snapshot != this.data){
10008             this.data = this.snapshot;
10009             delete this.snapshot;
10010             if(suppressEvent !== true){
10011                 this.fireEvent("datachanged", this);
10012             }
10013         }
10014     },
10015
10016     // private
10017     afterEdit : function(record){
10018         if(this.modified.indexOf(record) == -1){
10019             this.modified.push(record);
10020         }
10021         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10022     },
10023     
10024     // private
10025     afterReject : function(record){
10026         this.modified.remove(record);
10027         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10028     },
10029
10030     // private
10031     afterCommit : function(record){
10032         this.modified.remove(record);
10033         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10034     },
10035
10036     /**
10037      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10038      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10039      */
10040     commitChanges : function(){
10041         var m = this.modified.slice(0);
10042         this.modified = [];
10043         for(var i = 0, len = m.length; i < len; i++){
10044             m[i].commit();
10045         }
10046     },
10047
10048     /**
10049      * Cancel outstanding changes on all changed records.
10050      */
10051     rejectChanges : function(){
10052         var m = this.modified.slice(0);
10053         this.modified = [];
10054         for(var i = 0, len = m.length; i < len; i++){
10055             m[i].reject();
10056         }
10057     },
10058
10059     onMetaChange : function(meta, rtype, o){
10060         this.recordType = rtype;
10061         this.fields = rtype.prototype.fields;
10062         delete this.snapshot;
10063         this.sortInfo = meta.sortInfo || this.sortInfo;
10064         this.modified = [];
10065         this.fireEvent('metachange', this, this.reader.meta);
10066     },
10067     
10068     moveIndex : function(data, type)
10069     {
10070         var index = this.indexOf(data);
10071         
10072         var newIndex = index + type;
10073         
10074         this.remove(data);
10075         
10076         this.insert(newIndex, data);
10077         
10078     }
10079 });/*
10080  * Based on:
10081  * Ext JS Library 1.1.1
10082  * Copyright(c) 2006-2007, Ext JS, LLC.
10083  *
10084  * Originally Released Under LGPL - original licence link has changed is not relivant.
10085  *
10086  * Fork - LGPL
10087  * <script type="text/javascript">
10088  */
10089
10090 /**
10091  * @class Roo.data.SimpleStore
10092  * @extends Roo.data.Store
10093  * Small helper class to make creating Stores from Array data easier.
10094  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10095  * @cfg {Array} fields An array of field definition objects, or field name strings.
10096  * @cfg {Array} data The multi-dimensional array of data
10097  * @constructor
10098  * @param {Object} config
10099  */
10100 Roo.data.SimpleStore = function(config){
10101     Roo.data.SimpleStore.superclass.constructor.call(this, {
10102         isLocal : true,
10103         reader: new Roo.data.ArrayReader({
10104                 id: config.id
10105             },
10106             Roo.data.Record.create(config.fields)
10107         ),
10108         proxy : new Roo.data.MemoryProxy(config.data)
10109     });
10110     this.load();
10111 };
10112 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10113  * Based on:
10114  * Ext JS Library 1.1.1
10115  * Copyright(c) 2006-2007, Ext JS, LLC.
10116  *
10117  * Originally Released Under LGPL - original licence link has changed is not relivant.
10118  *
10119  * Fork - LGPL
10120  * <script type="text/javascript">
10121  */
10122
10123 /**
10124 /**
10125  * @extends Roo.data.Store
10126  * @class Roo.data.JsonStore
10127  * Small helper class to make creating Stores for JSON data easier. <br/>
10128 <pre><code>
10129 var store = new Roo.data.JsonStore({
10130     url: 'get-images.php',
10131     root: 'images',
10132     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10133 });
10134 </code></pre>
10135  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10136  * JsonReader and HttpProxy (unless inline data is provided).</b>
10137  * @cfg {Array} fields An array of field definition objects, or field name strings.
10138  * @constructor
10139  * @param {Object} config
10140  */
10141 Roo.data.JsonStore = function(c){
10142     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10143         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10144         reader: new Roo.data.JsonReader(c, c.fields)
10145     }));
10146 };
10147 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10148  * Based on:
10149  * Ext JS Library 1.1.1
10150  * Copyright(c) 2006-2007, Ext JS, LLC.
10151  *
10152  * Originally Released Under LGPL - original licence link has changed is not relivant.
10153  *
10154  * Fork - LGPL
10155  * <script type="text/javascript">
10156  */
10157
10158  
10159 Roo.data.Field = function(config){
10160     if(typeof config == "string"){
10161         config = {name: config};
10162     }
10163     Roo.apply(this, config);
10164     
10165     if(!this.type){
10166         this.type = "auto";
10167     }
10168     
10169     var st = Roo.data.SortTypes;
10170     // named sortTypes are supported, here we look them up
10171     if(typeof this.sortType == "string"){
10172         this.sortType = st[this.sortType];
10173     }
10174     
10175     // set default sortType for strings and dates
10176     if(!this.sortType){
10177         switch(this.type){
10178             case "string":
10179                 this.sortType = st.asUCString;
10180                 break;
10181             case "date":
10182                 this.sortType = st.asDate;
10183                 break;
10184             default:
10185                 this.sortType = st.none;
10186         }
10187     }
10188
10189     // define once
10190     var stripRe = /[\$,%]/g;
10191
10192     // prebuilt conversion function for this field, instead of
10193     // switching every time we're reading a value
10194     if(!this.convert){
10195         var cv, dateFormat = this.dateFormat;
10196         switch(this.type){
10197             case "":
10198             case "auto":
10199             case undefined:
10200                 cv = function(v){ return v; };
10201                 break;
10202             case "string":
10203                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10204                 break;
10205             case "int":
10206                 cv = function(v){
10207                     return v !== undefined && v !== null && v !== '' ?
10208                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10209                     };
10210                 break;
10211             case "float":
10212                 cv = function(v){
10213                     return v !== undefined && v !== null && v !== '' ?
10214                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10215                     };
10216                 break;
10217             case "bool":
10218             case "boolean":
10219                 cv = function(v){ return v === true || v === "true" || v == 1; };
10220                 break;
10221             case "date":
10222                 cv = function(v){
10223                     if(!v){
10224                         return '';
10225                     }
10226                     if(v instanceof Date){
10227                         return v;
10228                     }
10229                     if(dateFormat){
10230                         if(dateFormat == "timestamp"){
10231                             return new Date(v*1000);
10232                         }
10233                         return Date.parseDate(v, dateFormat);
10234                     }
10235                     var parsed = Date.parse(v);
10236                     return parsed ? new Date(parsed) : null;
10237                 };
10238              break;
10239             
10240         }
10241         this.convert = cv;
10242     }
10243 };
10244
10245 Roo.data.Field.prototype = {
10246     dateFormat: null,
10247     defaultValue: "",
10248     mapping: null,
10249     sortType : null,
10250     sortDir : "ASC"
10251 };/*
10252  * Based on:
10253  * Ext JS Library 1.1.1
10254  * Copyright(c) 2006-2007, Ext JS, LLC.
10255  *
10256  * Originally Released Under LGPL - original licence link has changed is not relivant.
10257  *
10258  * Fork - LGPL
10259  * <script type="text/javascript">
10260  */
10261  
10262 // Base class for reading structured data from a data source.  This class is intended to be
10263 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10264
10265 /**
10266  * @class Roo.data.DataReader
10267  * Base class for reading structured data from a data source.  This class is intended to be
10268  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10269  */
10270
10271 Roo.data.DataReader = function(meta, recordType){
10272     
10273     this.meta = meta;
10274     
10275     this.recordType = recordType instanceof Array ? 
10276         Roo.data.Record.create(recordType) : recordType;
10277 };
10278
10279 Roo.data.DataReader.prototype = {
10280      /**
10281      * Create an empty record
10282      * @param {Object} data (optional) - overlay some values
10283      * @return {Roo.data.Record} record created.
10284      */
10285     newRow :  function(d) {
10286         var da =  {};
10287         this.recordType.prototype.fields.each(function(c) {
10288             switch( c.type) {
10289                 case 'int' : da[c.name] = 0; break;
10290                 case 'date' : da[c.name] = new Date(); break;
10291                 case 'float' : da[c.name] = 0.0; break;
10292                 case 'boolean' : da[c.name] = false; break;
10293                 default : da[c.name] = ""; break;
10294             }
10295             
10296         });
10297         return new this.recordType(Roo.apply(da, d));
10298     }
10299     
10300 };/*
10301  * Based on:
10302  * Ext JS Library 1.1.1
10303  * Copyright(c) 2006-2007, Ext JS, LLC.
10304  *
10305  * Originally Released Under LGPL - original licence link has changed is not relivant.
10306  *
10307  * Fork - LGPL
10308  * <script type="text/javascript">
10309  */
10310
10311 /**
10312  * @class Roo.data.DataProxy
10313  * @extends Roo.data.Observable
10314  * This class is an abstract base class for implementations which provide retrieval of
10315  * unformatted data objects.<br>
10316  * <p>
10317  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10318  * (of the appropriate type which knows how to parse the data object) to provide a block of
10319  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10320  * <p>
10321  * Custom implementations must implement the load method as described in
10322  * {@link Roo.data.HttpProxy#load}.
10323  */
10324 Roo.data.DataProxy = function(){
10325     this.addEvents({
10326         /**
10327          * @event beforeload
10328          * Fires before a network request is made to retrieve a data object.
10329          * @param {Object} This DataProxy object.
10330          * @param {Object} params The params parameter to the load function.
10331          */
10332         beforeload : true,
10333         /**
10334          * @event load
10335          * Fires before the load method's callback is called.
10336          * @param {Object} This DataProxy object.
10337          * @param {Object} o The data object.
10338          * @param {Object} arg The callback argument object passed to the load function.
10339          */
10340         load : true,
10341         /**
10342          * @event loadexception
10343          * Fires if an Exception occurs during data retrieval.
10344          * @param {Object} This DataProxy object.
10345          * @param {Object} o The data object.
10346          * @param {Object} arg The callback argument object passed to the load function.
10347          * @param {Object} e The Exception.
10348          */
10349         loadexception : true
10350     });
10351     Roo.data.DataProxy.superclass.constructor.call(this);
10352 };
10353
10354 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10355
10356     /**
10357      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10358      */
10359 /*
10360  * Based on:
10361  * Ext JS Library 1.1.1
10362  * Copyright(c) 2006-2007, Ext JS, LLC.
10363  *
10364  * Originally Released Under LGPL - original licence link has changed is not relivant.
10365  *
10366  * Fork - LGPL
10367  * <script type="text/javascript">
10368  */
10369 /**
10370  * @class Roo.data.MemoryProxy
10371  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10372  * to the Reader when its load method is called.
10373  * @constructor
10374  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10375  */
10376 Roo.data.MemoryProxy = function(data){
10377     if (data.data) {
10378         data = data.data;
10379     }
10380     Roo.data.MemoryProxy.superclass.constructor.call(this);
10381     this.data = data;
10382 };
10383
10384 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10385     /**
10386      * Load data from the requested source (in this case an in-memory
10387      * data object passed to the constructor), read the data object into
10388      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10389      * process that block using the passed callback.
10390      * @param {Object} params This parameter is not used by the MemoryProxy class.
10391      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10392      * object into a block of Roo.data.Records.
10393      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10394      * The function must be passed <ul>
10395      * <li>The Record block object</li>
10396      * <li>The "arg" argument from the load function</li>
10397      * <li>A boolean success indicator</li>
10398      * </ul>
10399      * @param {Object} scope The scope in which to call the callback
10400      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10401      */
10402     load : function(params, reader, callback, scope, arg){
10403         params = params || {};
10404         var result;
10405         try {
10406             result = reader.readRecords(this.data);
10407         }catch(e){
10408             this.fireEvent("loadexception", this, arg, null, e);
10409             callback.call(scope, null, arg, false);
10410             return;
10411         }
10412         callback.call(scope, result, arg, true);
10413     },
10414     
10415     // private
10416     update : function(params, records){
10417         
10418     }
10419 });/*
10420  * Based on:
10421  * Ext JS Library 1.1.1
10422  * Copyright(c) 2006-2007, Ext JS, LLC.
10423  *
10424  * Originally Released Under LGPL - original licence link has changed is not relivant.
10425  *
10426  * Fork - LGPL
10427  * <script type="text/javascript">
10428  */
10429 /**
10430  * @class Roo.data.HttpProxy
10431  * @extends Roo.data.DataProxy
10432  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10433  * configured to reference a certain URL.<br><br>
10434  * <p>
10435  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10436  * from which the running page was served.<br><br>
10437  * <p>
10438  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10439  * <p>
10440  * Be aware that to enable the browser to parse an XML document, the server must set
10441  * the Content-Type header in the HTTP response to "text/xml".
10442  * @constructor
10443  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10444  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10445  * will be used to make the request.
10446  */
10447 Roo.data.HttpProxy = function(conn){
10448     Roo.data.HttpProxy.superclass.constructor.call(this);
10449     // is conn a conn config or a real conn?
10450     this.conn = conn;
10451     this.useAjax = !conn || !conn.events;
10452   
10453 };
10454
10455 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10456     // thse are take from connection...
10457     
10458     /**
10459      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10460      */
10461     /**
10462      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10463      * extra parameters to each request made by this object. (defaults to undefined)
10464      */
10465     /**
10466      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10467      *  to each request made by this object. (defaults to undefined)
10468      */
10469     /**
10470      * @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)
10471      */
10472     /**
10473      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10474      */
10475      /**
10476      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10477      * @type Boolean
10478      */
10479   
10480
10481     /**
10482      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10483      * @type Boolean
10484      */
10485     /**
10486      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10487      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10488      * a finer-grained basis than the DataProxy events.
10489      */
10490     getConnection : function(){
10491         return this.useAjax ? Roo.Ajax : this.conn;
10492     },
10493
10494     /**
10495      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10496      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10497      * process that block using the passed callback.
10498      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10499      * for the request to the remote server.
10500      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10501      * object into a block of Roo.data.Records.
10502      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10503      * The function must be passed <ul>
10504      * <li>The Record block object</li>
10505      * <li>The "arg" argument from the load function</li>
10506      * <li>A boolean success indicator</li>
10507      * </ul>
10508      * @param {Object} scope The scope in which to call the callback
10509      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10510      */
10511     load : function(params, reader, callback, scope, arg){
10512         if(this.fireEvent("beforeload", this, params) !== false){
10513             var  o = {
10514                 params : params || {},
10515                 request: {
10516                     callback : callback,
10517                     scope : scope,
10518                     arg : arg
10519                 },
10520                 reader: reader,
10521                 callback : this.loadResponse,
10522                 scope: this
10523             };
10524             if(this.useAjax){
10525                 Roo.applyIf(o, this.conn);
10526                 if(this.activeRequest){
10527                     Roo.Ajax.abort(this.activeRequest);
10528                 }
10529                 this.activeRequest = Roo.Ajax.request(o);
10530             }else{
10531                 this.conn.request(o);
10532             }
10533         }else{
10534             callback.call(scope||this, null, arg, false);
10535         }
10536     },
10537
10538     // private
10539     loadResponse : function(o, success, response){
10540         delete this.activeRequest;
10541         if(!success){
10542             this.fireEvent("loadexception", this, o, response);
10543             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10544             return;
10545         }
10546         var result;
10547         try {
10548             result = o.reader.read(response);
10549         }catch(e){
10550             this.fireEvent("loadexception", this, o, response, e);
10551             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10552             return;
10553         }
10554         
10555         this.fireEvent("load", this, o, o.request.arg);
10556         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10557     },
10558
10559     // private
10560     update : function(dataSet){
10561
10562     },
10563
10564     // private
10565     updateResponse : function(dataSet){
10566
10567     }
10568 });/*
10569  * Based on:
10570  * Ext JS Library 1.1.1
10571  * Copyright(c) 2006-2007, Ext JS, LLC.
10572  *
10573  * Originally Released Under LGPL - original licence link has changed is not relivant.
10574  *
10575  * Fork - LGPL
10576  * <script type="text/javascript">
10577  */
10578
10579 /**
10580  * @class Roo.data.ScriptTagProxy
10581  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10582  * other than the originating domain of the running page.<br><br>
10583  * <p>
10584  * <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
10585  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10586  * <p>
10587  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10588  * source code that is used as the source inside a &lt;script> tag.<br><br>
10589  * <p>
10590  * In order for the browser to process the returned data, the server must wrap the data object
10591  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10592  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10593  * depending on whether the callback name was passed:
10594  * <p>
10595  * <pre><code>
10596 boolean scriptTag = false;
10597 String cb = request.getParameter("callback");
10598 if (cb != null) {
10599     scriptTag = true;
10600     response.setContentType("text/javascript");
10601 } else {
10602     response.setContentType("application/x-json");
10603 }
10604 Writer out = response.getWriter();
10605 if (scriptTag) {
10606     out.write(cb + "(");
10607 }
10608 out.print(dataBlock.toJsonString());
10609 if (scriptTag) {
10610     out.write(");");
10611 }
10612 </pre></code>
10613  *
10614  * @constructor
10615  * @param {Object} config A configuration object.
10616  */
10617 Roo.data.ScriptTagProxy = function(config){
10618     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10619     Roo.apply(this, config);
10620     this.head = document.getElementsByTagName("head")[0];
10621 };
10622
10623 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10624
10625 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10626     /**
10627      * @cfg {String} url The URL from which to request the data object.
10628      */
10629     /**
10630      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10631      */
10632     timeout : 30000,
10633     /**
10634      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10635      * the server the name of the callback function set up by the load call to process the returned data object.
10636      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10637      * javascript output which calls this named function passing the data object as its only parameter.
10638      */
10639     callbackParam : "callback",
10640     /**
10641      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10642      * name to the request.
10643      */
10644     nocache : true,
10645
10646     /**
10647      * Load data from the configured URL, read the data object into
10648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10649      * process that block using the passed callback.
10650      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10651      * for the request to the remote server.
10652      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10653      * object into a block of Roo.data.Records.
10654      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10655      * The function must be passed <ul>
10656      * <li>The Record block object</li>
10657      * <li>The "arg" argument from the load function</li>
10658      * <li>A boolean success indicator</li>
10659      * </ul>
10660      * @param {Object} scope The scope in which to call the callback
10661      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10662      */
10663     load : function(params, reader, callback, scope, arg){
10664         if(this.fireEvent("beforeload", this, params) !== false){
10665
10666             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10667
10668             var url = this.url;
10669             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10670             if(this.nocache){
10671                 url += "&_dc=" + (new Date().getTime());
10672             }
10673             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10674             var trans = {
10675                 id : transId,
10676                 cb : "stcCallback"+transId,
10677                 scriptId : "stcScript"+transId,
10678                 params : params,
10679                 arg : arg,
10680                 url : url,
10681                 callback : callback,
10682                 scope : scope,
10683                 reader : reader
10684             };
10685             var conn = this;
10686
10687             window[trans.cb] = function(o){
10688                 conn.handleResponse(o, trans);
10689             };
10690
10691             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10692
10693             if(this.autoAbort !== false){
10694                 this.abort();
10695             }
10696
10697             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10698
10699             var script = document.createElement("script");
10700             script.setAttribute("src", url);
10701             script.setAttribute("type", "text/javascript");
10702             script.setAttribute("id", trans.scriptId);
10703             this.head.appendChild(script);
10704
10705             this.trans = trans;
10706         }else{
10707             callback.call(scope||this, null, arg, false);
10708         }
10709     },
10710
10711     // private
10712     isLoading : function(){
10713         return this.trans ? true : false;
10714     },
10715
10716     /**
10717      * Abort the current server request.
10718      */
10719     abort : function(){
10720         if(this.isLoading()){
10721             this.destroyTrans(this.trans);
10722         }
10723     },
10724
10725     // private
10726     destroyTrans : function(trans, isLoaded){
10727         this.head.removeChild(document.getElementById(trans.scriptId));
10728         clearTimeout(trans.timeoutId);
10729         if(isLoaded){
10730             window[trans.cb] = undefined;
10731             try{
10732                 delete window[trans.cb];
10733             }catch(e){}
10734         }else{
10735             // if hasn't been loaded, wait for load to remove it to prevent script error
10736             window[trans.cb] = function(){
10737                 window[trans.cb] = undefined;
10738                 try{
10739                     delete window[trans.cb];
10740                 }catch(e){}
10741             };
10742         }
10743     },
10744
10745     // private
10746     handleResponse : function(o, trans){
10747         this.trans = false;
10748         this.destroyTrans(trans, true);
10749         var result;
10750         try {
10751             result = trans.reader.readRecords(o);
10752         }catch(e){
10753             this.fireEvent("loadexception", this, o, trans.arg, e);
10754             trans.callback.call(trans.scope||window, null, trans.arg, false);
10755             return;
10756         }
10757         this.fireEvent("load", this, o, trans.arg);
10758         trans.callback.call(trans.scope||window, result, trans.arg, true);
10759     },
10760
10761     // private
10762     handleFailure : function(trans){
10763         this.trans = false;
10764         this.destroyTrans(trans, false);
10765         this.fireEvent("loadexception", this, null, trans.arg);
10766         trans.callback.call(trans.scope||window, null, trans.arg, false);
10767     }
10768 });/*
10769  * Based on:
10770  * Ext JS Library 1.1.1
10771  * Copyright(c) 2006-2007, Ext JS, LLC.
10772  *
10773  * Originally Released Under LGPL - original licence link has changed is not relivant.
10774  *
10775  * Fork - LGPL
10776  * <script type="text/javascript">
10777  */
10778
10779 /**
10780  * @class Roo.data.JsonReader
10781  * @extends Roo.data.DataReader
10782  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10783  * based on mappings in a provided Roo.data.Record constructor.
10784  * 
10785  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10786  * in the reply previously. 
10787  * 
10788  * <p>
10789  * Example code:
10790  * <pre><code>
10791 var RecordDef = Roo.data.Record.create([
10792     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10793     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10794 ]);
10795 var myReader = new Roo.data.JsonReader({
10796     totalProperty: "results",    // The property which contains the total dataset size (optional)
10797     root: "rows",                // The property which contains an Array of row objects
10798     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10799 }, RecordDef);
10800 </code></pre>
10801  * <p>
10802  * This would consume a JSON file like this:
10803  * <pre><code>
10804 { 'results': 2, 'rows': [
10805     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10806     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10807 }
10808 </code></pre>
10809  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10811  * paged from the remote server.
10812  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10813  * @cfg {String} root name of the property which contains the Array of row objects.
10814  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10815  * @cfg {Array} fields Array of field definition objects
10816  * @constructor
10817  * Create a new JsonReader
10818  * @param {Object} meta Metadata configuration options
10819  * @param {Object} recordType Either an Array of field definition objects,
10820  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10821  */
10822 Roo.data.JsonReader = function(meta, recordType){
10823     
10824     meta = meta || {};
10825     // set some defaults:
10826     Roo.applyIf(meta, {
10827         totalProperty: 'total',
10828         successProperty : 'success',
10829         root : 'data',
10830         id : 'id'
10831     });
10832     
10833     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10834 };
10835 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10836     
10837     /**
10838      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10839      * Used by Store query builder to append _requestMeta to params.
10840      * 
10841      */
10842     metaFromRemote : false,
10843     /**
10844      * This method is only used by a DataProxy which has retrieved data from a remote server.
10845      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10846      * @return {Object} data A data block which is used by an Roo.data.Store object as
10847      * a cache of Roo.data.Records.
10848      */
10849     read : function(response){
10850         var json = response.responseText;
10851        
10852         var o = /* eval:var:o */ eval("("+json+")");
10853         if(!o) {
10854             throw {message: "JsonReader.read: Json object not found"};
10855         }
10856         
10857         if(o.metaData){
10858             
10859             delete this.ef;
10860             this.metaFromRemote = true;
10861             this.meta = o.metaData;
10862             this.recordType = Roo.data.Record.create(o.metaData.fields);
10863             this.onMetaChange(this.meta, this.recordType, o);
10864         }
10865         return this.readRecords(o);
10866     },
10867
10868     // private function a store will implement
10869     onMetaChange : function(meta, recordType, o){
10870
10871     },
10872
10873     /**
10874          * @ignore
10875          */
10876     simpleAccess: function(obj, subsc) {
10877         return obj[subsc];
10878     },
10879
10880         /**
10881          * @ignore
10882          */
10883     getJsonAccessor: function(){
10884         var re = /[\[\.]/;
10885         return function(expr) {
10886             try {
10887                 return(re.test(expr))
10888                     ? new Function("obj", "return obj." + expr)
10889                     : function(obj){
10890                         return obj[expr];
10891                     };
10892             } catch(e){}
10893             return Roo.emptyFn;
10894         };
10895     }(),
10896
10897     /**
10898      * Create a data block containing Roo.data.Records from an XML document.
10899      * @param {Object} o An object which contains an Array of row objects in the property specified
10900      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10901      * which contains the total size of the dataset.
10902      * @return {Object} data A data block which is used by an Roo.data.Store object as
10903      * a cache of Roo.data.Records.
10904      */
10905     readRecords : function(o){
10906         /**
10907          * After any data loads, the raw JSON data is available for further custom processing.
10908          * @type Object
10909          */
10910         this.o = o;
10911         var s = this.meta, Record = this.recordType,
10912             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10913
10914 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10915         if (!this.ef) {
10916             if(s.totalProperty) {
10917                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10918                 }
10919                 if(s.successProperty) {
10920                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10921                 }
10922                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10923                 if (s.id) {
10924                         var g = this.getJsonAccessor(s.id);
10925                         this.getId = function(rec) {
10926                                 var r = g(rec);  
10927                                 return (r === undefined || r === "") ? null : r;
10928                         };
10929                 } else {
10930                         this.getId = function(){return null;};
10931                 }
10932             this.ef = [];
10933             for(var jj = 0; jj < fl; jj++){
10934                 f = fi[jj];
10935                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10936                 this.ef[jj] = this.getJsonAccessor(map);
10937             }
10938         }
10939
10940         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10941         if(s.totalProperty){
10942             var vt = parseInt(this.getTotal(o), 10);
10943             if(!isNaN(vt)){
10944                 totalRecords = vt;
10945             }
10946         }
10947         if(s.successProperty){
10948             var vs = this.getSuccess(o);
10949             if(vs === false || vs === 'false'){
10950                 success = false;
10951             }
10952         }
10953         var records = [];
10954         for(var i = 0; i < c; i++){
10955                 var n = root[i];
10956             var values = {};
10957             var id = this.getId(n);
10958             for(var j = 0; j < fl; j++){
10959                 f = fi[j];
10960             var v = this.ef[j](n);
10961             if (!f.convert) {
10962                 Roo.log('missing convert for ' + f.name);
10963                 Roo.log(f);
10964                 continue;
10965             }
10966             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10967             }
10968             var record = new Record(values, id);
10969             record.json = n;
10970             records[i] = record;
10971         }
10972         return {
10973             raw : o,
10974             success : success,
10975             records : records,
10976             totalRecords : totalRecords
10977         };
10978     }
10979 });/*
10980  * Based on:
10981  * Ext JS Library 1.1.1
10982  * Copyright(c) 2006-2007, Ext JS, LLC.
10983  *
10984  * Originally Released Under LGPL - original licence link has changed is not relivant.
10985  *
10986  * Fork - LGPL
10987  * <script type="text/javascript">
10988  */
10989
10990 /**
10991  * @class Roo.data.ArrayReader
10992  * @extends Roo.data.DataReader
10993  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10994  * Each element of that Array represents a row of data fields. The
10995  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10996  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10997  * <p>
10998  * Example code:.
10999  * <pre><code>
11000 var RecordDef = Roo.data.Record.create([
11001     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11002     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11003 ]);
11004 var myReader = new Roo.data.ArrayReader({
11005     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11006 }, RecordDef);
11007 </code></pre>
11008  * <p>
11009  * This would consume an Array like this:
11010  * <pre><code>
11011 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11012   </code></pre>
11013  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11014  * @constructor
11015  * Create a new JsonReader
11016  * @param {Object} meta Metadata configuration options.
11017  * @param {Object} recordType Either an Array of field definition objects
11018  * as specified to {@link Roo.data.Record#create},
11019  * or an {@link Roo.data.Record} object
11020  * created using {@link Roo.data.Record#create}.
11021  */
11022 Roo.data.ArrayReader = function(meta, recordType){
11023     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11024 };
11025
11026 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11027     /**
11028      * Create a data block containing Roo.data.Records from an XML document.
11029      * @param {Object} o An Array of row objects which represents the dataset.
11030      * @return {Object} data A data block which is used by an Roo.data.Store object as
11031      * a cache of Roo.data.Records.
11032      */
11033     readRecords : function(o){
11034         var sid = this.meta ? this.meta.id : null;
11035         var recordType = this.recordType, fields = recordType.prototype.fields;
11036         var records = [];
11037         var root = o;
11038             for(var i = 0; i < root.length; i++){
11039                     var n = root[i];
11040                 var values = {};
11041                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11042                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11043                 var f = fields.items[j];
11044                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11045                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11046                 v = f.convert(v);
11047                 values[f.name] = v;
11048             }
11049                 var record = new recordType(values, id);
11050                 record.json = n;
11051                 records[records.length] = record;
11052             }
11053             return {
11054                 records : records,
11055                 totalRecords : records.length
11056             };
11057     }
11058 });/*
11059  * - LGPL
11060  * * 
11061  */
11062
11063 /**
11064  * @class Roo.bootstrap.ComboBox
11065  * @extends Roo.bootstrap.TriggerField
11066  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11067  * @cfg {Boolean} append (true|false) default false
11068  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11069  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11070  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11071  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11072  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11073  * @cfg {Boolean} animate default true
11074  * @cfg {Boolean} emptyResultText only for touch device
11075  * @constructor
11076  * Create a new ComboBox.
11077  * @param {Object} config Configuration options
11078  */
11079 Roo.bootstrap.ComboBox = function(config){
11080     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11081     this.addEvents({
11082         /**
11083          * @event expand
11084          * Fires when the dropdown list is expanded
11085              * @param {Roo.bootstrap.ComboBox} combo This combo box
11086              */
11087         'expand' : true,
11088         /**
11089          * @event collapse
11090          * Fires when the dropdown list is collapsed
11091              * @param {Roo.bootstrap.ComboBox} combo This combo box
11092              */
11093         'collapse' : true,
11094         /**
11095          * @event beforeselect
11096          * Fires before a list item is selected. Return false to cancel the selection.
11097              * @param {Roo.bootstrap.ComboBox} combo This combo box
11098              * @param {Roo.data.Record} record The data record returned from the underlying store
11099              * @param {Number} index The index of the selected item in the dropdown list
11100              */
11101         'beforeselect' : true,
11102         /**
11103          * @event select
11104          * Fires when a list item is selected
11105              * @param {Roo.bootstrap.ComboBox} combo This combo box
11106              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11107              * @param {Number} index The index of the selected item in the dropdown list
11108              */
11109         'select' : true,
11110         /**
11111          * @event beforequery
11112          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11113          * The event object passed has these properties:
11114              * @param {Roo.bootstrap.ComboBox} combo This combo box
11115              * @param {String} query The query
11116              * @param {Boolean} forceAll true to force "all" query
11117              * @param {Boolean} cancel true to cancel the query
11118              * @param {Object} e The query event object
11119              */
11120         'beforequery': true,
11121          /**
11122          * @event add
11123          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11124              * @param {Roo.bootstrap.ComboBox} combo This combo box
11125              */
11126         'add' : true,
11127         /**
11128          * @event edit
11129          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11130              * @param {Roo.bootstrap.ComboBox} combo This combo box
11131              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11132              */
11133         'edit' : true,
11134         /**
11135          * @event remove
11136          * Fires when the remove value from the combobox array
11137              * @param {Roo.bootstrap.ComboBox} combo This combo box
11138              */
11139         'remove' : true,
11140         /**
11141          * @event specialfilter
11142          * Fires when specialfilter
11143             * @param {Roo.bootstrap.ComboBox} combo This combo box
11144             */
11145         'specialfilter' : true
11146         
11147     });
11148     
11149     this.item = [];
11150     this.tickItems = [];
11151     
11152     this.selectedIndex = -1;
11153     if(this.mode == 'local'){
11154         if(config.queryDelay === undefined){
11155             this.queryDelay = 10;
11156         }
11157         if(config.minChars === undefined){
11158             this.minChars = 0;
11159         }
11160     }
11161 };
11162
11163 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11164      
11165     /**
11166      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11167      * rendering into an Roo.Editor, defaults to false)
11168      */
11169     /**
11170      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11171      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11172      */
11173     /**
11174      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11175      */
11176     /**
11177      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11178      * the dropdown list (defaults to undefined, with no header element)
11179      */
11180
11181      /**
11182      * @cfg {String/Roo.Template} tpl The template to use to render the output
11183      */
11184      
11185      /**
11186      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11187      */
11188     listWidth: undefined,
11189     /**
11190      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11191      * mode = 'remote' or 'text' if mode = 'local')
11192      */
11193     displayField: undefined,
11194     
11195     /**
11196      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11197      * mode = 'remote' or 'value' if mode = 'local'). 
11198      * Note: use of a valueField requires the user make a selection
11199      * in order for a value to be mapped.
11200      */
11201     valueField: undefined,
11202     
11203     
11204     /**
11205      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11206      * field's data value (defaults to the underlying DOM element's name)
11207      */
11208     hiddenName: undefined,
11209     /**
11210      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11211      */
11212     listClass: '',
11213     /**
11214      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11215      */
11216     selectedClass: 'active',
11217     
11218     /**
11219      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11220      */
11221     shadow:'sides',
11222     /**
11223      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11224      * anchor positions (defaults to 'tl-bl')
11225      */
11226     listAlign: 'tl-bl?',
11227     /**
11228      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11229      */
11230     maxHeight: 300,
11231     /**
11232      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11233      * query specified by the allQuery config option (defaults to 'query')
11234      */
11235     triggerAction: 'query',
11236     /**
11237      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11238      * (defaults to 4, does not apply if editable = false)
11239      */
11240     minChars : 4,
11241     /**
11242      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11243      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11244      */
11245     typeAhead: false,
11246     /**
11247      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11248      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11249      */
11250     queryDelay: 500,
11251     /**
11252      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11253      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11254      */
11255     pageSize: 0,
11256     /**
11257      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11258      * when editable = true (defaults to false)
11259      */
11260     selectOnFocus:false,
11261     /**
11262      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11263      */
11264     queryParam: 'query',
11265     /**
11266      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11267      * when mode = 'remote' (defaults to 'Loading...')
11268      */
11269     loadingText: 'Loading...',
11270     /**
11271      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11272      */
11273     resizable: false,
11274     /**
11275      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11276      */
11277     handleHeight : 8,
11278     /**
11279      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11280      * traditional select (defaults to true)
11281      */
11282     editable: true,
11283     /**
11284      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11285      */
11286     allQuery: '',
11287     /**
11288      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11289      */
11290     mode: 'remote',
11291     /**
11292      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11293      * listWidth has a higher value)
11294      */
11295     minListWidth : 70,
11296     /**
11297      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11298      * allow the user to set arbitrary text into the field (defaults to false)
11299      */
11300     forceSelection:false,
11301     /**
11302      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11303      * if typeAhead = true (defaults to 250)
11304      */
11305     typeAheadDelay : 250,
11306     /**
11307      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11308      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11309      */
11310     valueNotFoundText : undefined,
11311     /**
11312      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11313      */
11314     blockFocus : false,
11315     
11316     /**
11317      * @cfg {Boolean} disableClear Disable showing of clear button.
11318      */
11319     disableClear : false,
11320     /**
11321      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11322      */
11323     alwaysQuery : false,
11324     
11325     /**
11326      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11327      */
11328     multiple : false,
11329     
11330     /**
11331      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11332      */
11333     invalidClass : "has-warning",
11334     
11335     /**
11336      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11337      */
11338     validClass : "has-success",
11339     
11340     /**
11341      * @cfg {Boolean} specialFilter (true|false) special filter default false
11342      */
11343     specialFilter : false,
11344     
11345     /**
11346      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11347      */
11348     mobileTouchView : true,
11349     
11350     //private
11351     addicon : false,
11352     editicon: false,
11353     
11354     page: 0,
11355     hasQuery: false,
11356     append: false,
11357     loadNext: false,
11358     autoFocus : true,
11359     tickable : false,
11360     btnPosition : 'right',
11361     triggerList : true,
11362     showToggleBtn : true,
11363     animate : true,
11364     emptyResultText: 'Empty',
11365     // element that contains real text value.. (when hidden is used..)
11366     
11367     getAutoCreate : function()
11368     {
11369         var cfg = false;
11370         
11371         /*
11372          * Touch Devices
11373          */
11374         
11375         if(Roo.isTouch && this.mobileTouchView){
11376             cfg = this.getAutoCreateTouchView();
11377             return cfg;;
11378         }
11379         
11380         /*
11381          *  Normal ComboBox
11382          */
11383         if(!this.tickable){
11384             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11385             return cfg;
11386         }
11387         
11388         /*
11389          *  ComboBox with tickable selections
11390          */
11391              
11392         var align = this.labelAlign || this.parentLabelAlign();
11393         
11394         cfg = {
11395             cls : 'form-group roo-combobox-tickable' //input-group
11396         };
11397         
11398         var buttons = {
11399             tag : 'div',
11400             cls : 'tickable-buttons',
11401             cn : [
11402                 {
11403                     tag : 'button',
11404                     type : 'button',
11405                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11406                     html : 'Edit'
11407                 },
11408                 {
11409                     tag : 'button',
11410                     type : 'button',
11411                     name : 'ok',
11412                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11413                     html : 'Done'
11414                 },
11415                 {
11416                     tag : 'button',
11417                     type : 'button',
11418                     name : 'cancel',
11419                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11420                     html : 'Cancel'
11421                 }
11422             ]
11423         };
11424         
11425         if(this.editable){
11426             buttons.cn.unshift({
11427                 tag: 'input',
11428                 cls: 'select2-search-field-input'
11429             });
11430         }
11431         
11432         var _this = this;
11433         
11434         Roo.each(buttons.cn, function(c){
11435             if (_this.size) {
11436                 c.cls += ' btn-' + _this.size;
11437             }
11438
11439             if (_this.disabled) {
11440                 c.disabled = true;
11441             }
11442         });
11443         
11444         var box = {
11445             tag: 'div',
11446             cn: [
11447                 {
11448                     tag: 'input',
11449                     type : 'hidden',
11450                     cls: 'form-hidden-field'
11451                 },
11452                 {
11453                     tag: 'ul',
11454                     cls: 'select2-choices',
11455                     cn:[
11456                         {
11457                             tag: 'li',
11458                             cls: 'select2-search-field',
11459                             cn: [
11460
11461                                 buttons
11462                             ]
11463                         }
11464                     ]
11465                 }
11466             ]
11467         }
11468         
11469         var combobox = {
11470             cls: 'select2-container input-group select2-container-multi',
11471             cn: [
11472                 box
11473 //                {
11474 //                    tag: 'ul',
11475 //                    cls: 'typeahead typeahead-long dropdown-menu',
11476 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11477 //                }
11478             ]
11479         };
11480         
11481         if(this.hasFeedback && !this.allowBlank){
11482             
11483             var feedback = {
11484                 tag: 'span',
11485                 cls: 'glyphicon form-control-feedback'
11486             };
11487
11488             combobox.cn.push(feedback);
11489         }
11490         
11491         if (align ==='left' && this.fieldLabel.length) {
11492             
11493                 Roo.log("left and has label");
11494                 cfg.cn = [
11495                     
11496                     {
11497                         tag: 'label',
11498                         'for' :  id,
11499                         cls : 'control-label col-sm-' + this.labelWidth,
11500                         html : this.fieldLabel
11501                         
11502                     },
11503                     {
11504                         cls : "col-sm-" + (12 - this.labelWidth), 
11505                         cn: [
11506                             combobox
11507                         ]
11508                     }
11509                     
11510                 ];
11511         } else if ( this.fieldLabel.length) {
11512                 Roo.log(" label");
11513                  cfg.cn = [
11514                    
11515                     {
11516                         tag: 'label',
11517                         //cls : 'input-group-addon',
11518                         html : this.fieldLabel
11519                         
11520                     },
11521                     
11522                     combobox
11523                     
11524                 ];
11525
11526         } else {
11527             
11528                 Roo.log(" no label && no align");
11529                 cfg = combobox
11530                      
11531                 
11532         }
11533          
11534         var settings=this;
11535         ['xs','sm','md','lg'].map(function(size){
11536             if (settings[size]) {
11537                 cfg.cls += ' col-' + size + '-' + settings[size];
11538             }
11539         });
11540         
11541         return cfg;
11542         
11543     },
11544     
11545     // private
11546     initEvents: function()
11547     {
11548         
11549         if (!this.store) {
11550             throw "can not find store for combo";
11551         }
11552         
11553         this.store = Roo.factory(this.store, Roo.data);
11554         
11555         /*
11556          * Touch Devices
11557          */
11558         
11559         if(Roo.isTouch && this.mobileTouchView){
11560             this.initTouchView();
11561             return;
11562         }
11563         
11564         if(this.tickable){
11565             this.initTickableEvents();
11566             return;
11567         }
11568         
11569         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11570         
11571         if(this.hiddenName){
11572             
11573             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11574             
11575             this.hiddenField.dom.value =
11576                 this.hiddenValue !== undefined ? this.hiddenValue :
11577                 this.value !== undefined ? this.value : '';
11578
11579             // prevent input submission
11580             this.el.dom.removeAttribute('name');
11581             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11582              
11583              
11584         }
11585         //if(Roo.isGecko){
11586         //    this.el.dom.setAttribute('autocomplete', 'off');
11587         //}
11588         
11589         var cls = 'x-combo-list';
11590         
11591         //this.list = new Roo.Layer({
11592         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11593         //});
11594         
11595         var _this = this;
11596         
11597         (function(){
11598             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11599             _this.list.setWidth(lw);
11600         }).defer(100);
11601         
11602         this.list.on('mouseover', this.onViewOver, this);
11603         this.list.on('mousemove', this.onViewMove, this);
11604         
11605         this.list.on('scroll', this.onViewScroll, this);
11606         
11607         /*
11608         this.list.swallowEvent('mousewheel');
11609         this.assetHeight = 0;
11610
11611         if(this.title){
11612             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11613             this.assetHeight += this.header.getHeight();
11614         }
11615
11616         this.innerList = this.list.createChild({cls:cls+'-inner'});
11617         this.innerList.on('mouseover', this.onViewOver, this);
11618         this.innerList.on('mousemove', this.onViewMove, this);
11619         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11620         
11621         if(this.allowBlank && !this.pageSize && !this.disableClear){
11622             this.footer = this.list.createChild({cls:cls+'-ft'});
11623             this.pageTb = new Roo.Toolbar(this.footer);
11624            
11625         }
11626         if(this.pageSize){
11627             this.footer = this.list.createChild({cls:cls+'-ft'});
11628             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11629                     {pageSize: this.pageSize});
11630             
11631         }
11632         
11633         if (this.pageTb && this.allowBlank && !this.disableClear) {
11634             var _this = this;
11635             this.pageTb.add(new Roo.Toolbar.Fill(), {
11636                 cls: 'x-btn-icon x-btn-clear',
11637                 text: '&#160;',
11638                 handler: function()
11639                 {
11640                     _this.collapse();
11641                     _this.clearValue();
11642                     _this.onSelect(false, -1);
11643                 }
11644             });
11645         }
11646         if (this.footer) {
11647             this.assetHeight += this.footer.getHeight();
11648         }
11649         */
11650             
11651         if(!this.tpl){
11652             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11653         }
11654
11655         this.view = new Roo.View(this.list, this.tpl, {
11656             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11657         });
11658         //this.view.wrapEl.setDisplayed(false);
11659         this.view.on('click', this.onViewClick, this);
11660         
11661         
11662         
11663         this.store.on('beforeload', this.onBeforeLoad, this);
11664         this.store.on('load', this.onLoad, this);
11665         this.store.on('loadexception', this.onLoadException, this);
11666         /*
11667         if(this.resizable){
11668             this.resizer = new Roo.Resizable(this.list,  {
11669                pinned:true, handles:'se'
11670             });
11671             this.resizer.on('resize', function(r, w, h){
11672                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11673                 this.listWidth = w;
11674                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11675                 this.restrictHeight();
11676             }, this);
11677             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11678         }
11679         */
11680         if(!this.editable){
11681             this.editable = true;
11682             this.setEditable(false);
11683         }
11684         
11685         /*
11686         
11687         if (typeof(this.events.add.listeners) != 'undefined') {
11688             
11689             this.addicon = this.wrap.createChild(
11690                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11691        
11692             this.addicon.on('click', function(e) {
11693                 this.fireEvent('add', this);
11694             }, this);
11695         }
11696         if (typeof(this.events.edit.listeners) != 'undefined') {
11697             
11698             this.editicon = this.wrap.createChild(
11699                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11700             if (this.addicon) {
11701                 this.editicon.setStyle('margin-left', '40px');
11702             }
11703             this.editicon.on('click', function(e) {
11704                 
11705                 // we fire even  if inothing is selected..
11706                 this.fireEvent('edit', this, this.lastData );
11707                 
11708             }, this);
11709         }
11710         */
11711         
11712         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11713             "up" : function(e){
11714                 this.inKeyMode = true;
11715                 this.selectPrev();
11716             },
11717
11718             "down" : function(e){
11719                 if(!this.isExpanded()){
11720                     this.onTriggerClick();
11721                 }else{
11722                     this.inKeyMode = true;
11723                     this.selectNext();
11724                 }
11725             },
11726
11727             "enter" : function(e){
11728 //                this.onViewClick();
11729                 //return true;
11730                 this.collapse();
11731                 
11732                 if(this.fireEvent("specialkey", this, e)){
11733                     this.onViewClick(false);
11734                 }
11735                 
11736                 return true;
11737             },
11738
11739             "esc" : function(e){
11740                 this.collapse();
11741             },
11742
11743             "tab" : function(e){
11744                 this.collapse();
11745                 
11746                 if(this.fireEvent("specialkey", this, e)){
11747                     this.onViewClick(false);
11748                 }
11749                 
11750                 return true;
11751             },
11752
11753             scope : this,
11754
11755             doRelay : function(foo, bar, hname){
11756                 if(hname == 'down' || this.scope.isExpanded()){
11757                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11758                 }
11759                 return true;
11760             },
11761
11762             forceKeyDown: true
11763         });
11764         
11765         
11766         this.queryDelay = Math.max(this.queryDelay || 10,
11767                 this.mode == 'local' ? 10 : 250);
11768         
11769         
11770         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11771         
11772         if(this.typeAhead){
11773             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11774         }
11775         if(this.editable !== false){
11776             this.inputEl().on("keyup", this.onKeyUp, this);
11777         }
11778         if(this.forceSelection){
11779             this.inputEl().on('blur', this.doForce, this);
11780         }
11781         
11782         if(this.multiple){
11783             this.choices = this.el.select('ul.select2-choices', true).first();
11784             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11785         }
11786     },
11787     
11788     initTickableEvents: function()
11789     {   
11790         this.createList();
11791         
11792         if(this.hiddenName){
11793             
11794             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11795             
11796             this.hiddenField.dom.value =
11797                 this.hiddenValue !== undefined ? this.hiddenValue :
11798                 this.value !== undefined ? this.value : '';
11799
11800             // prevent input submission
11801             this.el.dom.removeAttribute('name');
11802             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11803              
11804              
11805         }
11806         
11807 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11808         
11809         this.choices = this.el.select('ul.select2-choices', true).first();
11810         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11811         if(this.triggerList){
11812             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11813         }
11814          
11815         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11816         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11817         
11818         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11819         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11820         
11821         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11822         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11823         
11824         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11825         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11826         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11827         
11828         this.okBtn.hide();
11829         this.cancelBtn.hide();
11830         
11831         var _this = this;
11832         
11833         (function(){
11834             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11835             _this.list.setWidth(lw);
11836         }).defer(100);
11837         
11838         this.list.on('mouseover', this.onViewOver, this);
11839         this.list.on('mousemove', this.onViewMove, this);
11840         
11841         this.list.on('scroll', this.onViewScroll, this);
11842         
11843         if(!this.tpl){
11844             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>';
11845         }
11846
11847         this.view = new Roo.View(this.list, this.tpl, {
11848             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11849         });
11850         
11851         //this.view.wrapEl.setDisplayed(false);
11852         this.view.on('click', this.onViewClick, this);
11853         
11854         
11855         
11856         this.store.on('beforeload', this.onBeforeLoad, this);
11857         this.store.on('load', this.onLoad, this);
11858         this.store.on('loadexception', this.onLoadException, this);
11859         
11860         if(this.editable){
11861             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11862                 "up" : function(e){
11863                     this.inKeyMode = true;
11864                     this.selectPrev();
11865                 },
11866
11867                 "down" : function(e){
11868                     this.inKeyMode = true;
11869                     this.selectNext();
11870                 },
11871
11872                 "enter" : function(e){
11873                     if(this.fireEvent("specialkey", this, e)){
11874                         this.onViewClick(false);
11875                     }
11876                     
11877                     return true;
11878                 },
11879
11880                 "esc" : function(e){
11881                     this.onTickableFooterButtonClick(e, false, false);
11882                 },
11883
11884                 "tab" : function(e){
11885                     this.fireEvent("specialkey", this, e);
11886                     
11887                     this.onTickableFooterButtonClick(e, false, false);
11888                     
11889                     return true;
11890                 },
11891
11892                 scope : this,
11893
11894                 doRelay : function(e, fn, key){
11895                     if(this.scope.isExpanded()){
11896                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11897                     }
11898                     return true;
11899                 },
11900
11901                 forceKeyDown: true
11902             });
11903         }
11904         
11905         this.queryDelay = Math.max(this.queryDelay || 10,
11906                 this.mode == 'local' ? 10 : 250);
11907         
11908         
11909         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11910         
11911         if(this.typeAhead){
11912             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11913         }
11914         
11915         if(this.editable !== false){
11916             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11917         }
11918         
11919     },
11920
11921     onDestroy : function(){
11922         if(this.view){
11923             this.view.setStore(null);
11924             this.view.el.removeAllListeners();
11925             this.view.el.remove();
11926             this.view.purgeListeners();
11927         }
11928         if(this.list){
11929             this.list.dom.innerHTML  = '';
11930         }
11931         
11932         if(this.store){
11933             this.store.un('beforeload', this.onBeforeLoad, this);
11934             this.store.un('load', this.onLoad, this);
11935             this.store.un('loadexception', this.onLoadException, this);
11936         }
11937         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11938     },
11939
11940     // private
11941     fireKey : function(e){
11942         if(e.isNavKeyPress() && !this.list.isVisible()){
11943             this.fireEvent("specialkey", this, e);
11944         }
11945     },
11946
11947     // private
11948     onResize: function(w, h){
11949 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11950 //        
11951 //        if(typeof w != 'number'){
11952 //            // we do not handle it!?!?
11953 //            return;
11954 //        }
11955 //        var tw = this.trigger.getWidth();
11956 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11957 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11958 //        var x = w - tw;
11959 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11960 //            
11961 //        //this.trigger.setStyle('left', x+'px');
11962 //        
11963 //        if(this.list && this.listWidth === undefined){
11964 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11965 //            this.list.setWidth(lw);
11966 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11967 //        }
11968         
11969     
11970         
11971     },
11972
11973     /**
11974      * Allow or prevent the user from directly editing the field text.  If false is passed,
11975      * the user will only be able to select from the items defined in the dropdown list.  This method
11976      * is the runtime equivalent of setting the 'editable' config option at config time.
11977      * @param {Boolean} value True to allow the user to directly edit the field text
11978      */
11979     setEditable : function(value){
11980         if(value == this.editable){
11981             return;
11982         }
11983         this.editable = value;
11984         if(!value){
11985             this.inputEl().dom.setAttribute('readOnly', true);
11986             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11987             this.inputEl().addClass('x-combo-noedit');
11988         }else{
11989             this.inputEl().dom.setAttribute('readOnly', false);
11990             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11991             this.inputEl().removeClass('x-combo-noedit');
11992         }
11993     },
11994
11995     // private
11996     
11997     onBeforeLoad : function(combo,opts){
11998         if(!this.hasFocus){
11999             return;
12000         }
12001          if (!opts.add) {
12002             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12003          }
12004         this.restrictHeight();
12005         this.selectedIndex = -1;
12006     },
12007
12008     // private
12009     onLoad : function(){
12010         
12011         this.hasQuery = false;
12012         
12013         if(!this.hasFocus){
12014             return;
12015         }
12016         
12017         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12018             this.loading.hide();
12019         }
12020              
12021         if(this.store.getCount() > 0){
12022             this.expand();
12023             this.restrictHeight();
12024             if(this.lastQuery == this.allQuery){
12025                 if(this.editable && !this.tickable){
12026                     this.inputEl().dom.select();
12027                 }
12028                 
12029                 if(
12030                     !this.selectByValue(this.value, true) &&
12031                     this.autoFocus && 
12032                     (
12033                         !this.store.lastOptions ||
12034                         typeof(this.store.lastOptions.add) == 'undefined' || 
12035                         this.store.lastOptions.add != true
12036                     )
12037                 ){
12038                     this.select(0, true);
12039                 }
12040             }else{
12041                 if(this.autoFocus){
12042                     this.selectNext();
12043                 }
12044                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12045                     this.taTask.delay(this.typeAheadDelay);
12046                 }
12047             }
12048         }else{
12049             this.onEmptyResults();
12050         }
12051         
12052         //this.el.focus();
12053     },
12054     // private
12055     onLoadException : function()
12056     {
12057         this.hasQuery = false;
12058         
12059         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12060             this.loading.hide();
12061         }
12062         
12063         if(this.tickable && this.editable){
12064             return;
12065         }
12066         
12067         this.collapse();
12068         
12069         Roo.log(this.store.reader.jsonData);
12070         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12071             // fixme
12072             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12073         }
12074         
12075         
12076     },
12077     // private
12078     onTypeAhead : function(){
12079         if(this.store.getCount() > 0){
12080             var r = this.store.getAt(0);
12081             var newValue = r.data[this.displayField];
12082             var len = newValue.length;
12083             var selStart = this.getRawValue().length;
12084             
12085             if(selStart != len){
12086                 this.setRawValue(newValue);
12087                 this.selectText(selStart, newValue.length);
12088             }
12089         }
12090     },
12091
12092     // private
12093     onSelect : function(record, index){
12094         
12095         if(this.fireEvent('beforeselect', this, record, index) !== false){
12096         
12097             this.setFromData(index > -1 ? record.data : false);
12098             
12099             this.collapse();
12100             this.fireEvent('select', this, record, index);
12101         }
12102     },
12103
12104     /**
12105      * Returns the currently selected field value or empty string if no value is set.
12106      * @return {String} value The selected value
12107      */
12108     getValue : function(){
12109         
12110         if(this.multiple){
12111             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12112         }
12113         
12114         if(this.valueField){
12115             return typeof this.value != 'undefined' ? this.value : '';
12116         }else{
12117             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12118         }
12119     },
12120
12121     /**
12122      * Clears any text/value currently set in the field
12123      */
12124     clearValue : function(){
12125         if(this.hiddenField){
12126             this.hiddenField.dom.value = '';
12127         }
12128         this.value = '';
12129         this.setRawValue('');
12130         this.lastSelectionText = '';
12131         this.lastData = false;
12132         
12133         var close = this.closeTriggerEl();
12134         
12135         if(close){
12136             close.hide();
12137         }
12138         
12139     },
12140
12141     /**
12142      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12143      * will be displayed in the field.  If the value does not match the data value of an existing item,
12144      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12145      * Otherwise the field will be blank (although the value will still be set).
12146      * @param {String} value The value to match
12147      */
12148     setValue : function(v){
12149         if(this.multiple){
12150             this.syncValue();
12151             return;
12152         }
12153         
12154         var text = v;
12155         if(this.valueField){
12156             var r = this.findRecord(this.valueField, v);
12157             if(r){
12158                 text = r.data[this.displayField];
12159             }else if(this.valueNotFoundText !== undefined){
12160                 text = this.valueNotFoundText;
12161             }
12162         }
12163         this.lastSelectionText = text;
12164         if(this.hiddenField){
12165             this.hiddenField.dom.value = v;
12166         }
12167         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12168         this.value = v;
12169         
12170         var close = this.closeTriggerEl();
12171         
12172         if(close){
12173             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12174         }
12175     },
12176     /**
12177      * @property {Object} the last set data for the element
12178      */
12179     
12180     lastData : false,
12181     /**
12182      * Sets the value of the field based on a object which is related to the record format for the store.
12183      * @param {Object} value the value to set as. or false on reset?
12184      */
12185     setFromData : function(o){
12186         
12187         if(this.multiple){
12188             this.addItem(o);
12189             return;
12190         }
12191             
12192         var dv = ''; // display value
12193         var vv = ''; // value value..
12194         this.lastData = o;
12195         if (this.displayField) {
12196             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12197         } else {
12198             // this is an error condition!!!
12199             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12200         }
12201         
12202         if(this.valueField){
12203             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12204         }
12205         
12206         var close = this.closeTriggerEl();
12207         
12208         if(close){
12209             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12210         }
12211         
12212         if(this.hiddenField){
12213             this.hiddenField.dom.value = vv;
12214             
12215             this.lastSelectionText = dv;
12216             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12217             this.value = vv;
12218             return;
12219         }
12220         // no hidden field.. - we store the value in 'value', but still display
12221         // display field!!!!
12222         this.lastSelectionText = dv;
12223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12224         this.value = vv;
12225         
12226         
12227         
12228     },
12229     // private
12230     reset : function(){
12231         // overridden so that last data is reset..
12232         
12233         if(this.multiple){
12234             this.clearItem();
12235             return;
12236         }
12237         
12238         this.setValue(this.originalValue);
12239         this.clearInvalid();
12240         this.lastData = false;
12241         if (this.view) {
12242             this.view.clearSelections();
12243         }
12244     },
12245     // private
12246     findRecord : function(prop, value){
12247         var record;
12248         if(this.store.getCount() > 0){
12249             this.store.each(function(r){
12250                 if(r.data[prop] == value){
12251                     record = r;
12252                     return false;
12253                 }
12254                 return true;
12255             });
12256         }
12257         return record;
12258     },
12259     
12260     getName: function()
12261     {
12262         // returns hidden if it's set..
12263         if (!this.rendered) {return ''};
12264         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12265         
12266     },
12267     // private
12268     onViewMove : function(e, t){
12269         this.inKeyMode = false;
12270     },
12271
12272     // private
12273     onViewOver : function(e, t){
12274         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12275             return;
12276         }
12277         var item = this.view.findItemFromChild(t);
12278         
12279         if(item){
12280             var index = this.view.indexOf(item);
12281             this.select(index, false);
12282         }
12283     },
12284
12285     // private
12286     onViewClick : function(view, doFocus, el, e)
12287     {
12288         var index = this.view.getSelectedIndexes()[0];
12289         
12290         var r = this.store.getAt(index);
12291         
12292         if(this.tickable){
12293             
12294             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12295                 return;
12296             }
12297             
12298             var rm = false;
12299             var _this = this;
12300             
12301             Roo.each(this.tickItems, function(v,k){
12302                 
12303                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12304                     _this.tickItems.splice(k, 1);
12305                     
12306                     if(typeof(e) == 'undefined' && view == false){
12307                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12308                     }
12309                     
12310                     rm = true;
12311                     return;
12312                 }
12313             });
12314             
12315             if(rm){
12316                 return;
12317             }
12318             
12319             this.tickItems.push(r.data);
12320             
12321             if(typeof(e) == 'undefined' && view == false){
12322                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12323             }
12324                     
12325             return;
12326         }
12327         
12328         if(r){
12329             this.onSelect(r, index);
12330         }
12331         if(doFocus !== false && !this.blockFocus){
12332             this.inputEl().focus();
12333         }
12334     },
12335
12336     // private
12337     restrictHeight : function(){
12338         //this.innerList.dom.style.height = '';
12339         //var inner = this.innerList.dom;
12340         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12341         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12342         //this.list.beginUpdate();
12343         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12344         this.list.alignTo(this.inputEl(), this.listAlign);
12345         this.list.alignTo(this.inputEl(), this.listAlign);
12346         //this.list.endUpdate();
12347     },
12348
12349     // private
12350     onEmptyResults : function(){
12351         
12352         if(this.tickable && this.editable){
12353             this.restrictHeight();
12354             return;
12355         }
12356         
12357         this.collapse();
12358     },
12359
12360     /**
12361      * Returns true if the dropdown list is expanded, else false.
12362      */
12363     isExpanded : function(){
12364         return this.list.isVisible();
12365     },
12366
12367     /**
12368      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12369      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12370      * @param {String} value The data value of the item to select
12371      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12372      * selected item if it is not currently in view (defaults to true)
12373      * @return {Boolean} True if the value matched an item in the list, else false
12374      */
12375     selectByValue : function(v, scrollIntoView){
12376         if(v !== undefined && v !== null){
12377             var r = this.findRecord(this.valueField || this.displayField, v);
12378             if(r){
12379                 this.select(this.store.indexOf(r), scrollIntoView);
12380                 return true;
12381             }
12382         }
12383         return false;
12384     },
12385
12386     /**
12387      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12388      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12389      * @param {Number} index The zero-based index of the list item to select
12390      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12391      * selected item if it is not currently in view (defaults to true)
12392      */
12393     select : function(index, scrollIntoView){
12394         this.selectedIndex = index;
12395         this.view.select(index);
12396         if(scrollIntoView !== false){
12397             var el = this.view.getNode(index);
12398             /*
12399              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12400              */
12401             if(el){
12402                 this.list.scrollChildIntoView(el, false);
12403             }
12404         }
12405     },
12406
12407     // private
12408     selectNext : 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 < ct-1){
12414                 this.select(this.selectedIndex+1);
12415             }
12416         }
12417     },
12418
12419     // private
12420     selectPrev : function(){
12421         var ct = this.store.getCount();
12422         if(ct > 0){
12423             if(this.selectedIndex == -1){
12424                 this.select(0);
12425             }else if(this.selectedIndex != 0){
12426                 this.select(this.selectedIndex-1);
12427             }
12428         }
12429     },
12430
12431     // private
12432     onKeyUp : function(e){
12433         if(this.editable !== false && !e.isSpecialKey()){
12434             this.lastKey = e.getKey();
12435             this.dqTask.delay(this.queryDelay);
12436         }
12437     },
12438
12439     // private
12440     validateBlur : function(){
12441         return !this.list || !this.list.isVisible();   
12442     },
12443
12444     // private
12445     initQuery : function(){
12446         
12447         var v = this.getRawValue();
12448         
12449         if(this.tickable && this.editable){
12450             v = this.tickableInputEl().getValue();
12451         }
12452         
12453         this.doQuery(v);
12454     },
12455
12456     // private
12457     doForce : function(){
12458         if(this.inputEl().dom.value.length > 0){
12459             this.inputEl().dom.value =
12460                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12461              
12462         }
12463     },
12464
12465     /**
12466      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12467      * query allowing the query action to be canceled if needed.
12468      * @param {String} query The SQL query to execute
12469      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12470      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12471      * saved in the current store (defaults to false)
12472      */
12473     doQuery : function(q, forceAll){
12474         
12475         if(q === undefined || q === null){
12476             q = '';
12477         }
12478         var qe = {
12479             query: q,
12480             forceAll: forceAll,
12481             combo: this,
12482             cancel:false
12483         };
12484         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12485             return false;
12486         }
12487         q = qe.query;
12488         
12489         forceAll = qe.forceAll;
12490         if(forceAll === true || (q.length >= this.minChars)){
12491             
12492             this.hasQuery = true;
12493             
12494             if(this.lastQuery != q || this.alwaysQuery){
12495                 this.lastQuery = q;
12496                 if(this.mode == 'local'){
12497                     this.selectedIndex = -1;
12498                     if(forceAll){
12499                         this.store.clearFilter();
12500                     }else{
12501                         
12502                         if(this.specialFilter){
12503                             this.fireEvent('specialfilter', this);
12504                             this.onLoad();
12505                             return;
12506                         }
12507                         
12508                         this.store.filter(this.displayField, q);
12509                     }
12510                     
12511                     this.store.fireEvent("datachanged", this.store);
12512                     
12513                     this.onLoad();
12514                     
12515                     
12516                 }else{
12517                     
12518                     this.store.baseParams[this.queryParam] = q;
12519                     
12520                     var options = {params : this.getParams(q)};
12521                     
12522                     if(this.loadNext){
12523                         options.add = true;
12524                         options.params.start = this.page * this.pageSize;
12525                     }
12526                     
12527                     this.store.load(options);
12528                     
12529                     /*
12530                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12531                      *  we should expand the list on onLoad
12532                      *  so command out it
12533                      */
12534 //                    this.expand();
12535                 }
12536             }else{
12537                 this.selectedIndex = -1;
12538                 this.onLoad();   
12539             }
12540         }
12541         
12542         this.loadNext = false;
12543     },
12544     
12545     // private
12546     getParams : function(q){
12547         var p = {};
12548         //p[this.queryParam] = q;
12549         
12550         if(this.pageSize){
12551             p.start = 0;
12552             p.limit = this.pageSize;
12553         }
12554         return p;
12555     },
12556
12557     /**
12558      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12559      */
12560     collapse : function(){
12561         if(!this.isExpanded()){
12562             return;
12563         }
12564         
12565         this.list.hide();
12566         
12567         if(this.tickable){
12568             this.hasFocus = false;
12569             this.okBtn.hide();
12570             this.cancelBtn.hide();
12571             this.trigger.show();
12572             
12573             if(this.editable){
12574                 this.tickableInputEl().dom.value = '';
12575                 this.tickableInputEl().blur();
12576             }
12577             
12578         }
12579         
12580         Roo.get(document).un('mousedown', this.collapseIf, this);
12581         Roo.get(document).un('mousewheel', this.collapseIf, this);
12582         if (!this.editable) {
12583             Roo.get(document).un('keydown', this.listKeyPress, this);
12584         }
12585         this.fireEvent('collapse', this);
12586     },
12587
12588     // private
12589     collapseIf : function(e){
12590         var in_combo  = e.within(this.el);
12591         var in_list =  e.within(this.list);
12592         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12593         
12594         if (in_combo || in_list || is_list) {
12595             //e.stopPropagation();
12596             return;
12597         }
12598         
12599         if(this.tickable){
12600             this.onTickableFooterButtonClick(e, false, false);
12601         }
12602
12603         this.collapse();
12604         
12605     },
12606
12607     /**
12608      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12609      */
12610     expand : function(){
12611        
12612         if(this.isExpanded() || !this.hasFocus){
12613             return;
12614         }
12615         
12616         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12617         this.list.setWidth(lw);
12618         
12619         
12620          Roo.log('expand');
12621         
12622         this.list.show();
12623         
12624         this.restrictHeight();
12625         
12626         if(this.tickable){
12627             
12628             this.tickItems = Roo.apply([], this.item);
12629             
12630             this.okBtn.show();
12631             this.cancelBtn.show();
12632             this.trigger.hide();
12633             
12634             if(this.editable){
12635                 this.tickableInputEl().focus();
12636             }
12637             
12638         }
12639         
12640         Roo.get(document).on('mousedown', this.collapseIf, this);
12641         Roo.get(document).on('mousewheel', this.collapseIf, this);
12642         if (!this.editable) {
12643             Roo.get(document).on('keydown', this.listKeyPress, this);
12644         }
12645         
12646         this.fireEvent('expand', this);
12647     },
12648
12649     // private
12650     // Implements the default empty TriggerField.onTriggerClick function
12651     onTriggerClick : function(e)
12652     {
12653         Roo.log('trigger click');
12654         
12655         if(this.disabled || !this.triggerList){
12656             return;
12657         }
12658         
12659         this.page = 0;
12660         this.loadNext = false;
12661         
12662         if(this.isExpanded()){
12663             this.collapse();
12664             if (!this.blockFocus) {
12665                 this.inputEl().focus();
12666             }
12667             
12668         }else {
12669             this.hasFocus = true;
12670             if(this.triggerAction == 'all') {
12671                 this.doQuery(this.allQuery, true);
12672             } else {
12673                 this.doQuery(this.getRawValue());
12674             }
12675             if (!this.blockFocus) {
12676                 this.inputEl().focus();
12677             }
12678         }
12679     },
12680     
12681     onTickableTriggerClick : function(e)
12682     {
12683         if(this.disabled){
12684             return;
12685         }
12686         
12687         this.page = 0;
12688         this.loadNext = false;
12689         this.hasFocus = true;
12690         
12691         if(this.triggerAction == 'all') {
12692             this.doQuery(this.allQuery, true);
12693         } else {
12694             this.doQuery(this.getRawValue());
12695         }
12696     },
12697     
12698     onSearchFieldClick : function(e)
12699     {
12700         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12701             this.onTickableFooterButtonClick(e, false, false);
12702             return;
12703         }
12704         
12705         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12706             return;
12707         }
12708         
12709         this.page = 0;
12710         this.loadNext = false;
12711         this.hasFocus = true;
12712         
12713         if(this.triggerAction == 'all') {
12714             this.doQuery(this.allQuery, true);
12715         } else {
12716             this.doQuery(this.getRawValue());
12717         }
12718     },
12719     
12720     listKeyPress : function(e)
12721     {
12722         //Roo.log('listkeypress');
12723         // scroll to first matching element based on key pres..
12724         if (e.isSpecialKey()) {
12725             return false;
12726         }
12727         var k = String.fromCharCode(e.getKey()).toUpperCase();
12728         //Roo.log(k);
12729         var match  = false;
12730         var csel = this.view.getSelectedNodes();
12731         var cselitem = false;
12732         if (csel.length) {
12733             var ix = this.view.indexOf(csel[0]);
12734             cselitem  = this.store.getAt(ix);
12735             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12736                 cselitem = false;
12737             }
12738             
12739         }
12740         
12741         this.store.each(function(v) { 
12742             if (cselitem) {
12743                 // start at existing selection.
12744                 if (cselitem.id == v.id) {
12745                     cselitem = false;
12746                 }
12747                 return true;
12748             }
12749                 
12750             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12751                 match = this.store.indexOf(v);
12752                 return false;
12753             }
12754             return true;
12755         }, this);
12756         
12757         if (match === false) {
12758             return true; // no more action?
12759         }
12760         // scroll to?
12761         this.view.select(match);
12762         var sn = Roo.get(this.view.getSelectedNodes()[0])
12763         sn.scrollIntoView(sn.dom.parentNode, false);
12764     },
12765     
12766     onViewScroll : function(e, t){
12767         
12768         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){
12769             return;
12770         }
12771         
12772         this.hasQuery = true;
12773         
12774         this.loading = this.list.select('.loading', true).first();
12775         
12776         if(this.loading === null){
12777             this.list.createChild({
12778                 tag: 'div',
12779                 cls: 'loading select2-more-results select2-active',
12780                 html: 'Loading more results...'
12781             })
12782             
12783             this.loading = this.list.select('.loading', true).first();
12784             
12785             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12786             
12787             this.loading.hide();
12788         }
12789         
12790         this.loading.show();
12791         
12792         var _combo = this;
12793         
12794         this.page++;
12795         this.loadNext = true;
12796         
12797         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12798         
12799         return;
12800     },
12801     
12802     addItem : function(o)
12803     {   
12804         var dv = ''; // display value
12805         
12806         if (this.displayField) {
12807             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12808         } else {
12809             // this is an error condition!!!
12810             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12811         }
12812         
12813         if(!dv.length){
12814             return;
12815         }
12816         
12817         var choice = this.choices.createChild({
12818             tag: 'li',
12819             cls: 'select2-search-choice',
12820             cn: [
12821                 {
12822                     tag: 'div',
12823                     html: dv
12824                 },
12825                 {
12826                     tag: 'a',
12827                     href: '#',
12828                     cls: 'select2-search-choice-close',
12829                     tabindex: '-1'
12830                 }
12831             ]
12832             
12833         }, this.searchField);
12834         
12835         var close = choice.select('a.select2-search-choice-close', true).first()
12836         
12837         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12838         
12839         this.item.push(o);
12840         
12841         this.lastData = o;
12842         
12843         this.syncValue();
12844         
12845         this.inputEl().dom.value = '';
12846         
12847         this.validate();
12848     },
12849     
12850     onRemoveItem : function(e, _self, o)
12851     {
12852         e.preventDefault();
12853         
12854         this.lastItem = Roo.apply([], this.item);
12855         
12856         var index = this.item.indexOf(o.data) * 1;
12857         
12858         if( index < 0){
12859             Roo.log('not this item?!');
12860             return;
12861         }
12862         
12863         this.item.splice(index, 1);
12864         o.item.remove();
12865         
12866         this.syncValue();
12867         
12868         this.fireEvent('remove', this, e);
12869         
12870         this.validate();
12871         
12872     },
12873     
12874     syncValue : function()
12875     {
12876         if(!this.item.length){
12877             this.clearValue();
12878             return;
12879         }
12880             
12881         var value = [];
12882         var _this = this;
12883         Roo.each(this.item, function(i){
12884             if(_this.valueField){
12885                 value.push(i[_this.valueField]);
12886                 return;
12887             }
12888
12889             value.push(i);
12890         });
12891
12892         this.value = value.join(',');
12893
12894         if(this.hiddenField){
12895             this.hiddenField.dom.value = this.value;
12896         }
12897         
12898         this.store.fireEvent("datachanged", this.store);
12899     },
12900     
12901     clearItem : function()
12902     {
12903         if(!this.multiple){
12904             return;
12905         }
12906         
12907         this.item = [];
12908         
12909         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12910            c.remove();
12911         });
12912         
12913         this.syncValue();
12914         
12915         this.validate();
12916     },
12917     
12918     inputEl: function ()
12919     {
12920         if(Roo.isTouch && this.mobileTouchView){
12921             return this.el.select('input.form-control',true).first();
12922         }
12923         
12924         if(this.tickable){
12925             return this.searchField;
12926         }
12927         
12928         return this.el.select('input.form-control',true).first();
12929     },
12930     
12931     
12932     onTickableFooterButtonClick : function(e, btn, el)
12933     {
12934         e.preventDefault();
12935         
12936         this.lastItem = Roo.apply([], this.item);
12937         
12938         if(btn && btn.name == 'cancel'){
12939             this.tickItems = Roo.apply([], this.item);
12940             this.collapse();
12941             return;
12942         }
12943         
12944         this.clearItem();
12945         
12946         var _this = this;
12947         
12948         Roo.each(this.tickItems, function(o){
12949             _this.addItem(o);
12950         });
12951         
12952         this.collapse();
12953         
12954     },
12955     
12956     validate : function()
12957     {
12958         var v = this.getRawValue();
12959         
12960         if(this.multiple){
12961             v = this.getValue();
12962         }
12963         
12964         if(this.disabled || this.allowBlank || v.length){
12965             this.markValid();
12966             return true;
12967         }
12968         
12969         this.markInvalid();
12970         return false;
12971     },
12972     
12973     tickableInputEl : function()
12974     {
12975         if(!this.tickable || !this.editable){
12976             return this.inputEl();
12977         }
12978         
12979         return this.inputEl().select('.select2-search-field-input', true).first();
12980     },
12981     
12982     
12983     getAutoCreateTouchView : function()
12984     {
12985         var id = Roo.id();
12986         
12987         var cfg = {
12988             cls: 'form-group' //input-group
12989         };
12990         
12991         var input =  {
12992             tag: 'input',
12993             id : id,
12994             type : this.inputType,
12995             cls : 'form-control x-combo-noedit',
12996             autocomplete: 'new-password',
12997             placeholder : this.placeholder || '',
12998             readonly : true
12999         };
13000         
13001         if (this.name) {
13002             input.name = this.name;
13003         }
13004         
13005         if (this.size) {
13006             input.cls += ' input-' + this.size;
13007         }
13008         
13009         if (this.disabled) {
13010             input.disabled = true;
13011         }
13012         
13013         var inputblock = {
13014             cls : '',
13015             cn : [
13016                 input
13017             ]
13018         };
13019         
13020         if(this.before){
13021             inputblock.cls += ' input-group';
13022             
13023             inputblock.cn.unshift({
13024                 tag :'span',
13025                 cls : 'input-group-addon',
13026                 html : this.before
13027             });
13028         }
13029         
13030         if(this.removable && !this.multiple){
13031             inputblock.cls += ' roo-removable';
13032             
13033             inputblock.cn.push({
13034                 tag: 'button',
13035                 html : 'x',
13036                 cls : 'roo-combo-removable-btn close'
13037             });
13038         }
13039
13040         if(this.hasFeedback && !this.allowBlank){
13041             
13042             inputblock.cls += ' has-feedback';
13043             
13044             inputblock.cn.push({
13045                 tag: 'span',
13046                 cls: 'glyphicon form-control-feedback'
13047             });
13048             
13049         }
13050         
13051         if (this.after) {
13052             
13053             inputblock.cls += (this.before) ? '' : ' input-group';
13054             
13055             inputblock.cn.push({
13056                 tag :'span',
13057                 cls : 'input-group-addon',
13058                 html : this.after
13059             });
13060         }
13061
13062         var box = {
13063             tag: 'div',
13064             cn: [
13065                 {
13066                     tag: 'input',
13067                     type : 'hidden',
13068                     cls: 'form-hidden-field'
13069                 },
13070                 inputblock
13071             ]
13072             
13073         };
13074         
13075         if(this.multiple){
13076             box = {
13077                 tag: 'div',
13078                 cn: [
13079                     {
13080                         tag: 'input',
13081                         type : 'hidden',
13082                         cls: 'form-hidden-field'
13083                     },
13084                     {
13085                         tag: 'ul',
13086                         cls: 'select2-choices',
13087                         cn:[
13088                             {
13089                                 tag: 'li',
13090                                 cls: 'select2-search-field',
13091                                 cn: [
13092
13093                                     inputblock
13094                                 ]
13095                             }
13096                         ]
13097                     }
13098                 ]
13099             }
13100         };
13101         
13102         var combobox = {
13103             cls: 'select2-container input-group',
13104             cn: [
13105                 box
13106             ]
13107         };
13108         
13109         if(this.multiple){
13110             combobox.cls += ' select2-container-multi';
13111         }
13112         
13113         var align = this.labelAlign || this.parentLabelAlign();
13114         
13115         cfg.cn = combobox;
13116         
13117         if(this.fieldLabel.length){
13118             
13119             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13120             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13121             
13122             cfg.cn = [
13123                 {
13124                     tag: 'label',
13125                     cls : 'control-label ' + lw,
13126                     html : this.fieldLabel
13127
13128                 },
13129                 {
13130                     cls : cw, 
13131                     cn: [
13132                         combobox
13133                     ]
13134                 }
13135             ];
13136         }
13137         
13138         var settings = this;
13139         
13140         ['xs','sm','md','lg'].map(function(size){
13141             if (settings[size]) {
13142                 cfg.cls += ' col-' + size + '-' + settings[size];
13143             }
13144         });
13145         
13146         return cfg;
13147     },
13148     
13149     initTouchView : function()
13150     {
13151         this.renderTouchView();
13152         
13153         this.touchViewEl.on('scroll', function(){
13154             this.el.dom.scrollTop = 0;
13155         }, this);
13156         
13157         this.inputEl().on("click", this.showTouchView, this);
13158         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13159         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13160         
13161         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13162         
13163         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13164         this.store.on('load', this.onTouchViewLoad, this);
13165         this.store.on('loadexception', this.onTouchViewLoadException, this);
13166         
13167         if(this.hiddenName){
13168             
13169             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13170             
13171             this.hiddenField.dom.value =
13172                 this.hiddenValue !== undefined ? this.hiddenValue :
13173                 this.value !== undefined ? this.value : '';
13174         
13175             this.el.dom.removeAttribute('name');
13176             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13177         }
13178         
13179         if(this.multiple){
13180             this.choices = this.el.select('ul.select2-choices', true).first();
13181             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13182         }
13183         
13184         if(this.removable && !this.multiple){
13185             var close = this.closeTriggerEl();
13186             if(close){
13187                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13188                 close.on('click', this.removeBtnClick, this, close);
13189             }
13190         }
13191         
13192         return;
13193         
13194         
13195     },
13196     
13197     renderTouchView : function()
13198     {
13199         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13200         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13201         
13202         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13203         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13204         
13205         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13206         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13207         this.touchViewBodyEl.setStyle('overflow', 'auto');
13208         
13209         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13210         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13211         
13212         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13213         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13214         
13215     },
13216     
13217     showTouchView : function()
13218     {
13219         this.touchViewHeaderEl.hide();
13220
13221         if(this.fieldLabel.length){
13222             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13223             this.touchViewHeaderEl.show();
13224         }
13225
13226         this.touchViewEl.show();
13227
13228         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13229         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13230
13231         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13232
13233         if(this.fieldLabel.length){
13234             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13235         }
13236         
13237         this.touchViewBodyEl.setHeight(bodyHeight);
13238
13239         if(this.animate){
13240             var _this = this;
13241             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13242         }else{
13243             this.touchViewEl.addClass('in');
13244         }
13245
13246         this.doTouchViewQuery();
13247         
13248     },
13249     
13250     hideTouchView : function()
13251     {
13252         this.touchViewEl.removeClass('in');
13253
13254         if(this.animate){
13255             var _this = this;
13256             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13257         }else{
13258             this.touchViewEl.setStyle('display', 'none');
13259         }
13260         
13261     },
13262     
13263     setTouchViewValue : function()
13264     {
13265         if(this.multiple){
13266             this.clearItem();
13267         
13268             var _this = this;
13269
13270             Roo.each(this.tickItems, function(o){
13271                 this.addItem(o);
13272             }, this);
13273         }
13274         
13275         this.hideTouchView();
13276     },
13277     
13278     doTouchViewQuery : function()
13279     {
13280         var qe = {
13281             query: '',
13282             forceAll: true,
13283             combo: this,
13284             cancel:false
13285         };
13286         
13287         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13288             return false;
13289         }
13290         
13291         if(!this.alwaysQuery || this.mode == 'local'){
13292             this.onTouchViewLoad();
13293             return;
13294         }
13295         
13296         this.store.load();
13297     },
13298     
13299     onTouchViewBeforeLoad : function(combo,opts)
13300     {
13301         return;
13302     },
13303
13304     // private
13305     onTouchViewLoad : function()
13306     {
13307         if(this.store.getCount() < 1){
13308             this.onTouchViewEmptyResults();
13309             return;
13310         }
13311         
13312         this.clearTouchView();
13313         
13314         var rawValue = this.getRawValue();
13315         
13316         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13317         
13318         this.tickItems = [];
13319         
13320         this.store.data.each(function(d, rowIndex){
13321             var row = this.touchViewListGroup.createChild(template);
13322             
13323             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13324                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13325             }
13326             
13327             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13328                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13329             }
13330             
13331             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13332                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13333                 this.tickItems.push(d.data);
13334             }
13335             
13336             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13337             
13338         }, this);
13339         
13340         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13341         
13342         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13343
13344         if(this.fieldLabel.length){
13345             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13346         }
13347
13348         var listHeight = this.touchViewListGroup.getHeight();
13349         
13350         if(firstChecked && listHeight > bodyHeight){
13351             (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13352         }
13353         
13354     },
13355     
13356     onTouchViewLoadException : function()
13357     {
13358         this.hideTouchView();
13359     },
13360     
13361     onTouchViewEmptyResults : function()
13362     {
13363         this.clearTouchView();
13364         
13365         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13366         
13367         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13368         
13369     },
13370     
13371     clearTouchView : function()
13372     {
13373         this.touchViewListGroup.dom.innerHTML = '';
13374     },
13375     
13376     onTouchViewClick : function(e, el, o)
13377     {
13378         e.preventDefault();
13379         
13380         var row = o.row;
13381         var rowIndex = o.rowIndex;
13382         
13383         var r = this.store.getAt(rowIndex);
13384         
13385         if(!this.multiple){
13386             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13387                 c.dom.removeAttribute('checked');
13388             }, this);
13389             
13390             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13391         
13392             this.setFromData(r.data);
13393             
13394             var close = this.closeTriggerEl();
13395         
13396             if(close){
13397                 close.show();
13398             }
13399
13400             this.hideTouchView();
13401             
13402             this.fireEvent('select', this, r, rowIndex);
13403             
13404             return;
13405         }
13406         
13407         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13408             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13409             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13410             return;
13411         }
13412         
13413         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13414         this.addItem(r.data);
13415         this.tickItems.push(r.data);
13416         
13417     }
13418     
13419
13420     /** 
13421     * @cfg {Boolean} grow 
13422     * @hide 
13423     */
13424     /** 
13425     * @cfg {Number} growMin 
13426     * @hide 
13427     */
13428     /** 
13429     * @cfg {Number} growMax 
13430     * @hide 
13431     */
13432     /**
13433      * @hide
13434      * @method autoSize
13435      */
13436 });
13437
13438 Roo.apply(Roo.bootstrap.ComboBox,  {
13439     
13440     header : {
13441         tag: 'div',
13442         cls: 'modal-header',
13443         cn: [
13444             {
13445                 tag: 'h4',
13446                 cls: 'modal-title'
13447             }
13448         ]
13449     },
13450     
13451     body : {
13452         tag: 'div',
13453         cls: 'modal-body',
13454         cn: [
13455             {
13456                 tag: 'ul',
13457                 cls: 'list-group'
13458             }
13459         ]
13460     },
13461     
13462     listItemRadio : {
13463         tag: 'li',
13464         cls: 'list-group-item',
13465         cn: [
13466             {
13467                 tag: 'span',
13468                 cls: 'roo-combobox-list-group-item-value'
13469             },
13470             {
13471                 tag: 'div',
13472                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13473                 cn: [
13474                     {
13475                         tag: 'input',
13476                         type: 'radio'
13477                     },
13478                     {
13479                         tag: 'label'
13480                     }
13481                 ]
13482             }
13483         ]
13484     },
13485     
13486     listItemCheckbox : {
13487         tag: 'li',
13488         cls: 'list-group-item',
13489         cn: [
13490             {
13491                 tag: 'span',
13492                 cls: 'roo-combobox-list-group-item-value'
13493             },
13494             {
13495                 tag: 'div',
13496                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13497                 cn: [
13498                     {
13499                         tag: 'input',
13500                         type: 'checkbox'
13501                     },
13502                     {
13503                         tag: 'label'
13504                     }
13505                 ]
13506             }
13507         ]
13508     },
13509     
13510     emptyResult : {
13511         tag: 'div',
13512         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13513     },
13514     
13515     footer : {
13516         tag: 'div',
13517         cls: 'modal-footer',
13518         cn: [
13519             {
13520                 tag: 'div',
13521                 cls: 'row',
13522                 cn: [
13523                     {
13524                         tag: 'div',
13525                         cls: 'col-xs-6 text-left',
13526                         cn: {
13527                             tag: 'button',
13528                             cls: 'btn btn-danger roo-touch-view-cancel',
13529                             html: 'Cancel'
13530                         }
13531                     },
13532                     {
13533                         tag: 'div',
13534                         cls: 'col-xs-6 text-right',
13535                         cn: {
13536                             tag: 'button',
13537                             cls: 'btn btn-success roo-touch-view-ok',
13538                             html: 'OK'
13539                         }
13540                     }
13541                 ]
13542             }
13543         ]
13544         
13545     }
13546 });
13547
13548 Roo.apply(Roo.bootstrap.ComboBox,  {
13549     
13550     touchViewTemplate : {
13551         tag: 'div',
13552         cls: 'modal fade roo-combobox-touch-view',
13553         cn: [
13554             {
13555                 tag: 'div',
13556                 cls: 'modal-dialog',
13557                 cn: [
13558                     {
13559                         tag: 'div',
13560                         cls: 'modal-content',
13561                         cn: [
13562                             Roo.bootstrap.ComboBox.header,
13563                             Roo.bootstrap.ComboBox.body,
13564                             Roo.bootstrap.ComboBox.footer
13565                         ]
13566                     }
13567                 ]
13568             }
13569         ]
13570     }
13571 });/*
13572  * Based on:
13573  * Ext JS Library 1.1.1
13574  * Copyright(c) 2006-2007, Ext JS, LLC.
13575  *
13576  * Originally Released Under LGPL - original licence link has changed is not relivant.
13577  *
13578  * Fork - LGPL
13579  * <script type="text/javascript">
13580  */
13581
13582 /**
13583  * @class Roo.View
13584  * @extends Roo.util.Observable
13585  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13586  * This class also supports single and multi selection modes. <br>
13587  * Create a data model bound view:
13588  <pre><code>
13589  var store = new Roo.data.Store(...);
13590
13591  var view = new Roo.View({
13592     el : "my-element",
13593     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13594  
13595     singleSelect: true,
13596     selectedClass: "ydataview-selected",
13597     store: store
13598  });
13599
13600  // listen for node click?
13601  view.on("click", function(vw, index, node, e){
13602  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13603  });
13604
13605  // load XML data
13606  dataModel.load("foobar.xml");
13607  </code></pre>
13608  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13609  * <br><br>
13610  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13611  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13612  * 
13613  * Note: old style constructor is still suported (container, template, config)
13614  * 
13615  * @constructor
13616  * Create a new View
13617  * @param {Object} config The config object
13618  * 
13619  */
13620 Roo.View = function(config, depreciated_tpl, depreciated_config){
13621     
13622     this.parent = false;
13623     
13624     if (typeof(depreciated_tpl) == 'undefined') {
13625         // new way.. - universal constructor.
13626         Roo.apply(this, config);
13627         this.el  = Roo.get(this.el);
13628     } else {
13629         // old format..
13630         this.el  = Roo.get(config);
13631         this.tpl = depreciated_tpl;
13632         Roo.apply(this, depreciated_config);
13633     }
13634     this.wrapEl  = this.el.wrap().wrap();
13635     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13636     
13637     
13638     if(typeof(this.tpl) == "string"){
13639         this.tpl = new Roo.Template(this.tpl);
13640     } else {
13641         // support xtype ctors..
13642         this.tpl = new Roo.factory(this.tpl, Roo);
13643     }
13644     
13645     
13646     this.tpl.compile();
13647     
13648     /** @private */
13649     this.addEvents({
13650         /**
13651          * @event beforeclick
13652          * Fires before a click is processed. Returns false to cancel the default action.
13653          * @param {Roo.View} this
13654          * @param {Number} index The index of the target node
13655          * @param {HTMLElement} node The target node
13656          * @param {Roo.EventObject} e The raw event object
13657          */
13658             "beforeclick" : true,
13659         /**
13660          * @event click
13661          * Fires when a template node is clicked.
13662          * @param {Roo.View} this
13663          * @param {Number} index The index of the target node
13664          * @param {HTMLElement} node The target node
13665          * @param {Roo.EventObject} e The raw event object
13666          */
13667             "click" : true,
13668         /**
13669          * @event dblclick
13670          * Fires when a template node is double clicked.
13671          * @param {Roo.View} this
13672          * @param {Number} index The index of the target node
13673          * @param {HTMLElement} node The target node
13674          * @param {Roo.EventObject} e The raw event object
13675          */
13676             "dblclick" : true,
13677         /**
13678          * @event contextmenu
13679          * Fires when a template node is right clicked.
13680          * @param {Roo.View} this
13681          * @param {Number} index The index of the target node
13682          * @param {HTMLElement} node The target node
13683          * @param {Roo.EventObject} e The raw event object
13684          */
13685             "contextmenu" : true,
13686         /**
13687          * @event selectionchange
13688          * Fires when the selected nodes change.
13689          * @param {Roo.View} this
13690          * @param {Array} selections Array of the selected nodes
13691          */
13692             "selectionchange" : true,
13693     
13694         /**
13695          * @event beforeselect
13696          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13697          * @param {Roo.View} this
13698          * @param {HTMLElement} node The node to be selected
13699          * @param {Array} selections Array of currently selected nodes
13700          */
13701             "beforeselect" : true,
13702         /**
13703          * @event preparedata
13704          * Fires on every row to render, to allow you to change the data.
13705          * @param {Roo.View} this
13706          * @param {Object} data to be rendered (change this)
13707          */
13708           "preparedata" : true
13709           
13710           
13711         });
13712
13713
13714
13715     this.el.on({
13716         "click": this.onClick,
13717         "dblclick": this.onDblClick,
13718         "contextmenu": this.onContextMenu,
13719         scope:this
13720     });
13721
13722     this.selections = [];
13723     this.nodes = [];
13724     this.cmp = new Roo.CompositeElementLite([]);
13725     if(this.store){
13726         this.store = Roo.factory(this.store, Roo.data);
13727         this.setStore(this.store, true);
13728     }
13729     
13730     if ( this.footer && this.footer.xtype) {
13731            
13732          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13733         
13734         this.footer.dataSource = this.store
13735         this.footer.container = fctr;
13736         this.footer = Roo.factory(this.footer, Roo);
13737         fctr.insertFirst(this.el);
13738         
13739         // this is a bit insane - as the paging toolbar seems to detach the el..
13740 //        dom.parentNode.parentNode.parentNode
13741          // they get detached?
13742     }
13743     
13744     
13745     Roo.View.superclass.constructor.call(this);
13746     
13747     
13748 };
13749
13750 Roo.extend(Roo.View, Roo.util.Observable, {
13751     
13752      /**
13753      * @cfg {Roo.data.Store} store Data store to load data from.
13754      */
13755     store : false,
13756     
13757     /**
13758      * @cfg {String|Roo.Element} el The container element.
13759      */
13760     el : '',
13761     
13762     /**
13763      * @cfg {String|Roo.Template} tpl The template used by this View 
13764      */
13765     tpl : false,
13766     /**
13767      * @cfg {String} dataName the named area of the template to use as the data area
13768      *                          Works with domtemplates roo-name="name"
13769      */
13770     dataName: false,
13771     /**
13772      * @cfg {String} selectedClass The css class to add to selected nodes
13773      */
13774     selectedClass : "x-view-selected",
13775      /**
13776      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13777      */
13778     emptyText : "",
13779     
13780     /**
13781      * @cfg {String} text to display on mask (default Loading)
13782      */
13783     mask : false,
13784     /**
13785      * @cfg {Boolean} multiSelect Allow multiple selection
13786      */
13787     multiSelect : false,
13788     /**
13789      * @cfg {Boolean} singleSelect Allow single selection
13790      */
13791     singleSelect:  false,
13792     
13793     /**
13794      * @cfg {Boolean} toggleSelect - selecting 
13795      */
13796     toggleSelect : false,
13797     
13798     /**
13799      * @cfg {Boolean} tickable - selecting 
13800      */
13801     tickable : false,
13802     
13803     /**
13804      * Returns the element this view is bound to.
13805      * @return {Roo.Element}
13806      */
13807     getEl : function(){
13808         return this.wrapEl;
13809     },
13810     
13811     
13812
13813     /**
13814      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13815      */
13816     refresh : function(){
13817         //Roo.log('refresh');
13818         var t = this.tpl;
13819         
13820         // if we are using something like 'domtemplate', then
13821         // the what gets used is:
13822         // t.applySubtemplate(NAME, data, wrapping data..)
13823         // the outer template then get' applied with
13824         //     the store 'extra data'
13825         // and the body get's added to the
13826         //      roo-name="data" node?
13827         //      <span class='roo-tpl-{name}'></span> ?????
13828         
13829         
13830         
13831         this.clearSelections();
13832         this.el.update("");
13833         var html = [];
13834         var records = this.store.getRange();
13835         if(records.length < 1) {
13836             
13837             // is this valid??  = should it render a template??
13838             
13839             this.el.update(this.emptyText);
13840             return;
13841         }
13842         var el = this.el;
13843         if (this.dataName) {
13844             this.el.update(t.apply(this.store.meta)); //????
13845             el = this.el.child('.roo-tpl-' + this.dataName);
13846         }
13847         
13848         for(var i = 0, len = records.length; i < len; i++){
13849             var data = this.prepareData(records[i].data, i, records[i]);
13850             this.fireEvent("preparedata", this, data, i, records[i]);
13851             
13852             var d = Roo.apply({}, data);
13853             
13854             if(this.tickable){
13855                 Roo.apply(d, {'roo-id' : Roo.id()});
13856                 
13857                 var _this = this;
13858             
13859                 Roo.each(this.parent.item, function(item){
13860                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13861                         return;
13862                     }
13863                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13864                 });
13865             }
13866             
13867             html[html.length] = Roo.util.Format.trim(
13868                 this.dataName ?
13869                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13870                     t.apply(d)
13871             );
13872         }
13873         
13874         
13875         
13876         el.update(html.join(""));
13877         this.nodes = el.dom.childNodes;
13878         this.updateIndexes(0);
13879     },
13880     
13881
13882     /**
13883      * Function to override to reformat the data that is sent to
13884      * the template for each node.
13885      * DEPRICATED - use the preparedata event handler.
13886      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13887      * a JSON object for an UpdateManager bound view).
13888      */
13889     prepareData : function(data, index, record)
13890     {
13891         this.fireEvent("preparedata", this, data, index, record);
13892         return data;
13893     },
13894
13895     onUpdate : function(ds, record){
13896         // Roo.log('on update');   
13897         this.clearSelections();
13898         var index = this.store.indexOf(record);
13899         var n = this.nodes[index];
13900         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13901         n.parentNode.removeChild(n);
13902         this.updateIndexes(index, index);
13903     },
13904
13905     
13906     
13907 // --------- FIXME     
13908     onAdd : function(ds, records, index)
13909     {
13910         //Roo.log(['on Add', ds, records, index] );        
13911         this.clearSelections();
13912         if(this.nodes.length == 0){
13913             this.refresh();
13914             return;
13915         }
13916         var n = this.nodes[index];
13917         for(var i = 0, len = records.length; i < len; i++){
13918             var d = this.prepareData(records[i].data, i, records[i]);
13919             if(n){
13920                 this.tpl.insertBefore(n, d);
13921             }else{
13922                 
13923                 this.tpl.append(this.el, d);
13924             }
13925         }
13926         this.updateIndexes(index);
13927     },
13928
13929     onRemove : function(ds, record, index){
13930        // Roo.log('onRemove');
13931         this.clearSelections();
13932         var el = this.dataName  ?
13933             this.el.child('.roo-tpl-' + this.dataName) :
13934             this.el; 
13935         
13936         el.dom.removeChild(this.nodes[index]);
13937         this.updateIndexes(index);
13938     },
13939
13940     /**
13941      * Refresh an individual node.
13942      * @param {Number} index
13943      */
13944     refreshNode : function(index){
13945         this.onUpdate(this.store, this.store.getAt(index));
13946     },
13947
13948     updateIndexes : function(startIndex, endIndex){
13949         var ns = this.nodes;
13950         startIndex = startIndex || 0;
13951         endIndex = endIndex || ns.length - 1;
13952         for(var i = startIndex; i <= endIndex; i++){
13953             ns[i].nodeIndex = i;
13954         }
13955     },
13956
13957     /**
13958      * Changes the data store this view uses and refresh the view.
13959      * @param {Store} store
13960      */
13961     setStore : function(store, initial){
13962         if(!initial && this.store){
13963             this.store.un("datachanged", this.refresh);
13964             this.store.un("add", this.onAdd);
13965             this.store.un("remove", this.onRemove);
13966             this.store.un("update", this.onUpdate);
13967             this.store.un("clear", this.refresh);
13968             this.store.un("beforeload", this.onBeforeLoad);
13969             this.store.un("load", this.onLoad);
13970             this.store.un("loadexception", this.onLoad);
13971         }
13972         if(store){
13973           
13974             store.on("datachanged", this.refresh, this);
13975             store.on("add", this.onAdd, this);
13976             store.on("remove", this.onRemove, this);
13977             store.on("update", this.onUpdate, this);
13978             store.on("clear", this.refresh, this);
13979             store.on("beforeload", this.onBeforeLoad, this);
13980             store.on("load", this.onLoad, this);
13981             store.on("loadexception", this.onLoad, this);
13982         }
13983         
13984         if(store){
13985             this.refresh();
13986         }
13987     },
13988     /**
13989      * onbeforeLoad - masks the loading area.
13990      *
13991      */
13992     onBeforeLoad : function(store,opts)
13993     {
13994          //Roo.log('onBeforeLoad');   
13995         if (!opts.add) {
13996             this.el.update("");
13997         }
13998         this.el.mask(this.mask ? this.mask : "Loading" ); 
13999     },
14000     onLoad : function ()
14001     {
14002         this.el.unmask();
14003     },
14004     
14005
14006     /**
14007      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14008      * @param {HTMLElement} node
14009      * @return {HTMLElement} The template node
14010      */
14011     findItemFromChild : function(node){
14012         var el = this.dataName  ?
14013             this.el.child('.roo-tpl-' + this.dataName,true) :
14014             this.el.dom; 
14015         
14016         if(!node || node.parentNode == el){
14017                     return node;
14018             }
14019             var p = node.parentNode;
14020             while(p && p != el){
14021             if(p.parentNode == el){
14022                 return p;
14023             }
14024             p = p.parentNode;
14025         }
14026             return null;
14027     },
14028
14029     /** @ignore */
14030     onClick : function(e){
14031         var item = this.findItemFromChild(e.getTarget());
14032         if(item){
14033             var index = this.indexOf(item);
14034             if(this.onItemClick(item, index, e) !== false){
14035                 this.fireEvent("click", this, index, item, e);
14036             }
14037         }else{
14038             this.clearSelections();
14039         }
14040     },
14041
14042     /** @ignore */
14043     onContextMenu : function(e){
14044         var item = this.findItemFromChild(e.getTarget());
14045         if(item){
14046             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14047         }
14048     },
14049
14050     /** @ignore */
14051     onDblClick : function(e){
14052         var item = this.findItemFromChild(e.getTarget());
14053         if(item){
14054             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14055         }
14056     },
14057
14058     onItemClick : function(item, index, e)
14059     {
14060         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14061             return false;
14062         }
14063         if (this.toggleSelect) {
14064             var m = this.isSelected(item) ? 'unselect' : 'select';
14065             //Roo.log(m);
14066             var _t = this;
14067             _t[m](item, true, false);
14068             return true;
14069         }
14070         if(this.multiSelect || this.singleSelect){
14071             if(this.multiSelect && e.shiftKey && this.lastSelection){
14072                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14073             }else{
14074                 this.select(item, this.multiSelect && e.ctrlKey);
14075                 this.lastSelection = item;
14076             }
14077             
14078             if(!this.tickable){
14079                 e.preventDefault();
14080             }
14081             
14082         }
14083         return true;
14084     },
14085
14086     /**
14087      * Get the number of selected nodes.
14088      * @return {Number}
14089      */
14090     getSelectionCount : function(){
14091         return this.selections.length;
14092     },
14093
14094     /**
14095      * Get the currently selected nodes.
14096      * @return {Array} An array of HTMLElements
14097      */
14098     getSelectedNodes : function(){
14099         return this.selections;
14100     },
14101
14102     /**
14103      * Get the indexes of the selected nodes.
14104      * @return {Array}
14105      */
14106     getSelectedIndexes : function(){
14107         var indexes = [], s = this.selections;
14108         for(var i = 0, len = s.length; i < len; i++){
14109             indexes.push(s[i].nodeIndex);
14110         }
14111         return indexes;
14112     },
14113
14114     /**
14115      * Clear all selections
14116      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14117      */
14118     clearSelections : function(suppressEvent){
14119         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14120             this.cmp.elements = this.selections;
14121             this.cmp.removeClass(this.selectedClass);
14122             this.selections = [];
14123             if(!suppressEvent){
14124                 this.fireEvent("selectionchange", this, this.selections);
14125             }
14126         }
14127     },
14128
14129     /**
14130      * Returns true if the passed node is selected
14131      * @param {HTMLElement/Number} node The node or node index
14132      * @return {Boolean}
14133      */
14134     isSelected : function(node){
14135         var s = this.selections;
14136         if(s.length < 1){
14137             return false;
14138         }
14139         node = this.getNode(node);
14140         return s.indexOf(node) !== -1;
14141     },
14142
14143     /**
14144      * Selects nodes.
14145      * @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
14146      * @param {Boolean} keepExisting (optional) true to keep existing selections
14147      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14148      */
14149     select : function(nodeInfo, keepExisting, suppressEvent){
14150         if(nodeInfo instanceof Array){
14151             if(!keepExisting){
14152                 this.clearSelections(true);
14153             }
14154             for(var i = 0, len = nodeInfo.length; i < len; i++){
14155                 this.select(nodeInfo[i], true, true);
14156             }
14157             return;
14158         } 
14159         var node = this.getNode(nodeInfo);
14160         if(!node || this.isSelected(node)){
14161             return; // already selected.
14162         }
14163         if(!keepExisting){
14164             this.clearSelections(true);
14165         }
14166         
14167         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14168             Roo.fly(node).addClass(this.selectedClass);
14169             this.selections.push(node);
14170             if(!suppressEvent){
14171                 this.fireEvent("selectionchange", this, this.selections);
14172             }
14173         }
14174         
14175         
14176     },
14177       /**
14178      * Unselects nodes.
14179      * @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
14180      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14181      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14182      */
14183     unselect : function(nodeInfo, keepExisting, suppressEvent)
14184     {
14185         if(nodeInfo instanceof Array){
14186             Roo.each(this.selections, function(s) {
14187                 this.unselect(s, nodeInfo);
14188             }, this);
14189             return;
14190         }
14191         var node = this.getNode(nodeInfo);
14192         if(!node || !this.isSelected(node)){
14193             //Roo.log("not selected");
14194             return; // not selected.
14195         }
14196         // fireevent???
14197         var ns = [];
14198         Roo.each(this.selections, function(s) {
14199             if (s == node ) {
14200                 Roo.fly(node).removeClass(this.selectedClass);
14201
14202                 return;
14203             }
14204             ns.push(s);
14205         },this);
14206         
14207         this.selections= ns;
14208         this.fireEvent("selectionchange", this, this.selections);
14209     },
14210
14211     /**
14212      * Gets a template node.
14213      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14214      * @return {HTMLElement} The node or null if it wasn't found
14215      */
14216     getNode : function(nodeInfo){
14217         if(typeof nodeInfo == "string"){
14218             return document.getElementById(nodeInfo);
14219         }else if(typeof nodeInfo == "number"){
14220             return this.nodes[nodeInfo];
14221         }
14222         return nodeInfo;
14223     },
14224
14225     /**
14226      * Gets a range template nodes.
14227      * @param {Number} startIndex
14228      * @param {Number} endIndex
14229      * @return {Array} An array of nodes
14230      */
14231     getNodes : function(start, end){
14232         var ns = this.nodes;
14233         start = start || 0;
14234         end = typeof end == "undefined" ? ns.length - 1 : end;
14235         var nodes = [];
14236         if(start <= end){
14237             for(var i = start; i <= end; i++){
14238                 nodes.push(ns[i]);
14239             }
14240         } else{
14241             for(var i = start; i >= end; i--){
14242                 nodes.push(ns[i]);
14243             }
14244         }
14245         return nodes;
14246     },
14247
14248     /**
14249      * Finds the index of the passed node
14250      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14251      * @return {Number} The index of the node or -1
14252      */
14253     indexOf : function(node){
14254         node = this.getNode(node);
14255         if(typeof node.nodeIndex == "number"){
14256             return node.nodeIndex;
14257         }
14258         var ns = this.nodes;
14259         for(var i = 0, len = ns.length; i < len; i++){
14260             if(ns[i] == node){
14261                 return i;
14262             }
14263         }
14264         return -1;
14265     }
14266 });
14267 /*
14268  * - LGPL
14269  *
14270  * based on jquery fullcalendar
14271  * 
14272  */
14273
14274 Roo.bootstrap = Roo.bootstrap || {};
14275 /**
14276  * @class Roo.bootstrap.Calendar
14277  * @extends Roo.bootstrap.Component
14278  * Bootstrap Calendar class
14279  * @cfg {Boolean} loadMask (true|false) default false
14280  * @cfg {Object} header generate the user specific header of the calendar, default false
14281
14282  * @constructor
14283  * Create a new Container
14284  * @param {Object} config The config object
14285  */
14286
14287
14288
14289 Roo.bootstrap.Calendar = function(config){
14290     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14291      this.addEvents({
14292         /**
14293              * @event select
14294              * Fires when a date is selected
14295              * @param {DatePicker} this
14296              * @param {Date} date The selected date
14297              */
14298         'select': true,
14299         /**
14300              * @event monthchange
14301              * Fires when the displayed month changes 
14302              * @param {DatePicker} this
14303              * @param {Date} date The selected month
14304              */
14305         'monthchange': true,
14306         /**
14307              * @event evententer
14308              * Fires when mouse over an event
14309              * @param {Calendar} this
14310              * @param {event} Event
14311              */
14312         'evententer': true,
14313         /**
14314              * @event eventleave
14315              * Fires when the mouse leaves an
14316              * @param {Calendar} this
14317              * @param {event}
14318              */
14319         'eventleave': true,
14320         /**
14321              * @event eventclick
14322              * Fires when the mouse click an
14323              * @param {Calendar} this
14324              * @param {event}
14325              */
14326         'eventclick': true
14327         
14328     });
14329
14330 };
14331
14332 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14333     
14334      /**
14335      * @cfg {Number} startDay
14336      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14337      */
14338     startDay : 0,
14339     
14340     loadMask : false,
14341     
14342     header : false,
14343       
14344     getAutoCreate : function(){
14345         
14346         
14347         var fc_button = function(name, corner, style, content ) {
14348             return Roo.apply({},{
14349                 tag : 'span',
14350                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14351                          (corner.length ?
14352                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14353                             ''
14354                         ),
14355                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14356                 unselectable: 'on'
14357             });
14358         };
14359         
14360         var header = {};
14361         
14362         if(!this.header){
14363             header = {
14364                 tag : 'table',
14365                 cls : 'fc-header',
14366                 style : 'width:100%',
14367                 cn : [
14368                     {
14369                         tag: 'tr',
14370                         cn : [
14371                             {
14372                                 tag : 'td',
14373                                 cls : 'fc-header-left',
14374                                 cn : [
14375                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14376                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14377                                     { tag: 'span', cls: 'fc-header-space' },
14378                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14379
14380
14381                                 ]
14382                             },
14383
14384                             {
14385                                 tag : 'td',
14386                                 cls : 'fc-header-center',
14387                                 cn : [
14388                                     {
14389                                         tag: 'span',
14390                                         cls: 'fc-header-title',
14391                                         cn : {
14392                                             tag: 'H2',
14393                                             html : 'month / year'
14394                                         }
14395                                     }
14396
14397                                 ]
14398                             },
14399                             {
14400                                 tag : 'td',
14401                                 cls : 'fc-header-right',
14402                                 cn : [
14403                               /*      fc_button('month', 'left', '', 'month' ),
14404                                     fc_button('week', '', '', 'week' ),
14405                                     fc_button('day', 'right', '', 'day' )
14406                                 */    
14407
14408                                 ]
14409                             }
14410
14411                         ]
14412                     }
14413                 ]
14414             };
14415         }
14416         
14417         header = this.header;
14418         
14419        
14420         var cal_heads = function() {
14421             var ret = [];
14422             // fixme - handle this.
14423             
14424             for (var i =0; i < Date.dayNames.length; i++) {
14425                 var d = Date.dayNames[i];
14426                 ret.push({
14427                     tag: 'th',
14428                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14429                     html : d.substring(0,3)
14430                 });
14431                 
14432             }
14433             ret[0].cls += ' fc-first';
14434             ret[6].cls += ' fc-last';
14435             return ret;
14436         };
14437         var cal_cell = function(n) {
14438             return  {
14439                 tag: 'td',
14440                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14441                 cn : [
14442                     {
14443                         cn : [
14444                             {
14445                                 cls: 'fc-day-number',
14446                                 html: 'D'
14447                             },
14448                             {
14449                                 cls: 'fc-day-content',
14450                              
14451                                 cn : [
14452                                      {
14453                                         style: 'position: relative;' // height: 17px;
14454                                     }
14455                                 ]
14456                             }
14457                             
14458                             
14459                         ]
14460                     }
14461                 ]
14462                 
14463             }
14464         };
14465         var cal_rows = function() {
14466             
14467             var ret = [];
14468             for (var r = 0; r < 6; r++) {
14469                 var row= {
14470                     tag : 'tr',
14471                     cls : 'fc-week',
14472                     cn : []
14473                 };
14474                 
14475                 for (var i =0; i < Date.dayNames.length; i++) {
14476                     var d = Date.dayNames[i];
14477                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14478
14479                 }
14480                 row.cn[0].cls+=' fc-first';
14481                 row.cn[0].cn[0].style = 'min-height:90px';
14482                 row.cn[6].cls+=' fc-last';
14483                 ret.push(row);
14484                 
14485             }
14486             ret[0].cls += ' fc-first';
14487             ret[4].cls += ' fc-prev-last';
14488             ret[5].cls += ' fc-last';
14489             return ret;
14490             
14491         };
14492         
14493         var cal_table = {
14494             tag: 'table',
14495             cls: 'fc-border-separate',
14496             style : 'width:100%',
14497             cellspacing  : 0,
14498             cn : [
14499                 { 
14500                     tag: 'thead',
14501                     cn : [
14502                         { 
14503                             tag: 'tr',
14504                             cls : 'fc-first fc-last',
14505                             cn : cal_heads()
14506                         }
14507                     ]
14508                 },
14509                 { 
14510                     tag: 'tbody',
14511                     cn : cal_rows()
14512                 }
14513                   
14514             ]
14515         };
14516          
14517          var cfg = {
14518             cls : 'fc fc-ltr',
14519             cn : [
14520                 header,
14521                 {
14522                     cls : 'fc-content',
14523                     style : "position: relative;",
14524                     cn : [
14525                         {
14526                             cls : 'fc-view fc-view-month fc-grid',
14527                             style : 'position: relative',
14528                             unselectable : 'on',
14529                             cn : [
14530                                 {
14531                                     cls : 'fc-event-container',
14532                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14533                                 },
14534                                 cal_table
14535                             ]
14536                         }
14537                     ]
14538     
14539                 }
14540            ] 
14541             
14542         };
14543         
14544          
14545         
14546         return cfg;
14547     },
14548     
14549     
14550     initEvents : function()
14551     {
14552         if(!this.store){
14553             throw "can not find store for calendar";
14554         }
14555         
14556         var mark = {
14557             tag: "div",
14558             cls:"x-dlg-mask",
14559             style: "text-align:center",
14560             cn: [
14561                 {
14562                     tag: "div",
14563                     style: "background-color:white;width:50%;margin:250 auto",
14564                     cn: [
14565                         {
14566                             tag: "img",
14567                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14568                         },
14569                         {
14570                             tag: "span",
14571                             html: "Loading"
14572                         }
14573                         
14574                     ]
14575                 }
14576             ]
14577         }
14578         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14579         
14580         var size = this.el.select('.fc-content', true).first().getSize();
14581         this.maskEl.setSize(size.width, size.height);
14582         this.maskEl.enableDisplayMode("block");
14583         if(!this.loadMask){
14584             this.maskEl.hide();
14585         }
14586         
14587         this.store = Roo.factory(this.store, Roo.data);
14588         this.store.on('load', this.onLoad, this);
14589         this.store.on('beforeload', this.onBeforeLoad, this);
14590         
14591         this.resize();
14592         
14593         this.cells = this.el.select('.fc-day',true);
14594         //Roo.log(this.cells);
14595         this.textNodes = this.el.query('.fc-day-number');
14596         this.cells.addClassOnOver('fc-state-hover');
14597         
14598         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14599         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14600         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14601         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14602         
14603         this.on('monthchange', this.onMonthChange, this);
14604         
14605         this.update(new Date().clearTime());
14606     },
14607     
14608     resize : function() {
14609         var sz  = this.el.getSize();
14610         
14611         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14612         this.el.select('.fc-day-content div',true).setHeight(34);
14613     },
14614     
14615     
14616     // private
14617     showPrevMonth : function(e){
14618         this.update(this.activeDate.add("mo", -1));
14619     },
14620     showToday : function(e){
14621         this.update(new Date().clearTime());
14622     },
14623     // private
14624     showNextMonth : function(e){
14625         this.update(this.activeDate.add("mo", 1));
14626     },
14627
14628     // private
14629     showPrevYear : function(){
14630         this.update(this.activeDate.add("y", -1));
14631     },
14632
14633     // private
14634     showNextYear : function(){
14635         this.update(this.activeDate.add("y", 1));
14636     },
14637
14638     
14639    // private
14640     update : function(date)
14641     {
14642         var vd = this.activeDate;
14643         this.activeDate = date;
14644 //        if(vd && this.el){
14645 //            var t = date.getTime();
14646 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14647 //                Roo.log('using add remove');
14648 //                
14649 //                this.fireEvent('monthchange', this, date);
14650 //                
14651 //                this.cells.removeClass("fc-state-highlight");
14652 //                this.cells.each(function(c){
14653 //                   if(c.dateValue == t){
14654 //                       c.addClass("fc-state-highlight");
14655 //                       setTimeout(function(){
14656 //                            try{c.dom.firstChild.focus();}catch(e){}
14657 //                       }, 50);
14658 //                       return false;
14659 //                   }
14660 //                   return true;
14661 //                });
14662 //                return;
14663 //            }
14664 //        }
14665         
14666         var days = date.getDaysInMonth();
14667         
14668         var firstOfMonth = date.getFirstDateOfMonth();
14669         var startingPos = firstOfMonth.getDay()-this.startDay;
14670         
14671         if(startingPos < this.startDay){
14672             startingPos += 7;
14673         }
14674         
14675         var pm = date.add(Date.MONTH, -1);
14676         var prevStart = pm.getDaysInMonth()-startingPos;
14677 //        
14678         this.cells = this.el.select('.fc-day',true);
14679         this.textNodes = this.el.query('.fc-day-number');
14680         this.cells.addClassOnOver('fc-state-hover');
14681         
14682         var cells = this.cells.elements;
14683         var textEls = this.textNodes;
14684         
14685         Roo.each(cells, function(cell){
14686             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14687         });
14688         
14689         days += startingPos;
14690
14691         // convert everything to numbers so it's fast
14692         var day = 86400000;
14693         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14694         //Roo.log(d);
14695         //Roo.log(pm);
14696         //Roo.log(prevStart);
14697         
14698         var today = new Date().clearTime().getTime();
14699         var sel = date.clearTime().getTime();
14700         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14701         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14702         var ddMatch = this.disabledDatesRE;
14703         var ddText = this.disabledDatesText;
14704         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14705         var ddaysText = this.disabledDaysText;
14706         var format = this.format;
14707         
14708         var setCellClass = function(cal, cell){
14709             cell.row = 0;
14710             cell.events = [];
14711             cell.more = [];
14712             //Roo.log('set Cell Class');
14713             cell.title = "";
14714             var t = d.getTime();
14715             
14716             //Roo.log(d);
14717             
14718             cell.dateValue = t;
14719             if(t == today){
14720                 cell.className += " fc-today";
14721                 cell.className += " fc-state-highlight";
14722                 cell.title = cal.todayText;
14723             }
14724             if(t == sel){
14725                 // disable highlight in other month..
14726                 //cell.className += " fc-state-highlight";
14727                 
14728             }
14729             // disabling
14730             if(t < min) {
14731                 cell.className = " fc-state-disabled";
14732                 cell.title = cal.minText;
14733                 return;
14734             }
14735             if(t > max) {
14736                 cell.className = " fc-state-disabled";
14737                 cell.title = cal.maxText;
14738                 return;
14739             }
14740             if(ddays){
14741                 if(ddays.indexOf(d.getDay()) != -1){
14742                     cell.title = ddaysText;
14743                     cell.className = " fc-state-disabled";
14744                 }
14745             }
14746             if(ddMatch && format){
14747                 var fvalue = d.dateFormat(format);
14748                 if(ddMatch.test(fvalue)){
14749                     cell.title = ddText.replace("%0", fvalue);
14750                     cell.className = " fc-state-disabled";
14751                 }
14752             }
14753             
14754             if (!cell.initialClassName) {
14755                 cell.initialClassName = cell.dom.className;
14756             }
14757             
14758             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14759         };
14760
14761         var i = 0;
14762         
14763         for(; i < startingPos; i++) {
14764             textEls[i].innerHTML = (++prevStart);
14765             d.setDate(d.getDate()+1);
14766             
14767             cells[i].className = "fc-past fc-other-month";
14768             setCellClass(this, cells[i]);
14769         }
14770         
14771         var intDay = 0;
14772         
14773         for(; i < days; i++){
14774             intDay = i - startingPos + 1;
14775             textEls[i].innerHTML = (intDay);
14776             d.setDate(d.getDate()+1);
14777             
14778             cells[i].className = ''; // "x-date-active";
14779             setCellClass(this, cells[i]);
14780         }
14781         var extraDays = 0;
14782         
14783         for(; i < 42; i++) {
14784             textEls[i].innerHTML = (++extraDays);
14785             d.setDate(d.getDate()+1);
14786             
14787             cells[i].className = "fc-future fc-other-month";
14788             setCellClass(this, cells[i]);
14789         }
14790         
14791         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14792         
14793         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14794         
14795         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14796         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14797         
14798         if(totalRows != 6){
14799             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14800             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14801         }
14802         
14803         this.fireEvent('monthchange', this, date);
14804         
14805         
14806         /*
14807         if(!this.internalRender){
14808             var main = this.el.dom.firstChild;
14809             var w = main.offsetWidth;
14810             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14811             Roo.fly(main).setWidth(w);
14812             this.internalRender = true;
14813             // opera does not respect the auto grow header center column
14814             // then, after it gets a width opera refuses to recalculate
14815             // without a second pass
14816             if(Roo.isOpera && !this.secondPass){
14817                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14818                 this.secondPass = true;
14819                 this.update.defer(10, this, [date]);
14820             }
14821         }
14822         */
14823         
14824     },
14825     
14826     findCell : function(dt) {
14827         dt = dt.clearTime().getTime();
14828         var ret = false;
14829         this.cells.each(function(c){
14830             //Roo.log("check " +c.dateValue + '?=' + dt);
14831             if(c.dateValue == dt){
14832                 ret = c;
14833                 return false;
14834             }
14835             return true;
14836         });
14837         
14838         return ret;
14839     },
14840     
14841     findCells : function(ev) {
14842         var s = ev.start.clone().clearTime().getTime();
14843        // Roo.log(s);
14844         var e= ev.end.clone().clearTime().getTime();
14845        // Roo.log(e);
14846         var ret = [];
14847         this.cells.each(function(c){
14848              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14849             
14850             if(c.dateValue > e){
14851                 return ;
14852             }
14853             if(c.dateValue < s){
14854                 return ;
14855             }
14856             ret.push(c);
14857         });
14858         
14859         return ret;    
14860     },
14861     
14862 //    findBestRow: function(cells)
14863 //    {
14864 //        var ret = 0;
14865 //        
14866 //        for (var i =0 ; i < cells.length;i++) {
14867 //            ret  = Math.max(cells[i].rows || 0,ret);
14868 //        }
14869 //        return ret;
14870 //        
14871 //    },
14872     
14873     
14874     addItem : function(ev)
14875     {
14876         // look for vertical location slot in
14877         var cells = this.findCells(ev);
14878         
14879 //        ev.row = this.findBestRow(cells);
14880         
14881         // work out the location.
14882         
14883         var crow = false;
14884         var rows = [];
14885         for(var i =0; i < cells.length; i++) {
14886             
14887             cells[i].row = cells[0].row;
14888             
14889             if(i == 0){
14890                 cells[i].row = cells[i].row + 1;
14891             }
14892             
14893             if (!crow) {
14894                 crow = {
14895                     start : cells[i],
14896                     end :  cells[i]
14897                 };
14898                 continue;
14899             }
14900             if (crow.start.getY() == cells[i].getY()) {
14901                 // on same row.
14902                 crow.end = cells[i];
14903                 continue;
14904             }
14905             // different row.
14906             rows.push(crow);
14907             crow = {
14908                 start: cells[i],
14909                 end : cells[i]
14910             };
14911             
14912         }
14913         
14914         rows.push(crow);
14915         ev.els = [];
14916         ev.rows = rows;
14917         ev.cells = cells;
14918         
14919         cells[0].events.push(ev);
14920         
14921         this.calevents.push(ev);
14922     },
14923     
14924     clearEvents: function() {
14925         
14926         if(!this.calevents){
14927             return;
14928         }
14929         
14930         Roo.each(this.cells.elements, function(c){
14931             c.row = 0;
14932             c.events = [];
14933             c.more = [];
14934         });
14935         
14936         Roo.each(this.calevents, function(e) {
14937             Roo.each(e.els, function(el) {
14938                 el.un('mouseenter' ,this.onEventEnter, this);
14939                 el.un('mouseleave' ,this.onEventLeave, this);
14940                 el.remove();
14941             },this);
14942         },this);
14943         
14944         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14945             e.remove();
14946         });
14947         
14948     },
14949     
14950     renderEvents: function()
14951     {   
14952         var _this = this;
14953         
14954         this.cells.each(function(c) {
14955             
14956             if(c.row < 5){
14957                 return;
14958             }
14959             
14960             var ev = c.events;
14961             
14962             var r = 4;
14963             if(c.row != c.events.length){
14964                 r = 4 - (4 - (c.row - c.events.length));
14965             }
14966             
14967             c.events = ev.slice(0, r);
14968             c.more = ev.slice(r);
14969             
14970             if(c.more.length && c.more.length == 1){
14971                 c.events.push(c.more.pop());
14972             }
14973             
14974             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14975             
14976         });
14977             
14978         this.cells.each(function(c) {
14979             
14980             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14981             
14982             
14983             for (var e = 0; e < c.events.length; e++){
14984                 var ev = c.events[e];
14985                 var rows = ev.rows;
14986                 
14987                 for(var i = 0; i < rows.length; i++) {
14988                 
14989                     // how many rows should it span..
14990
14991                     var  cfg = {
14992                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14993                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14994
14995                         unselectable : "on",
14996                         cn : [
14997                             {
14998                                 cls: 'fc-event-inner',
14999                                 cn : [
15000     //                                {
15001     //                                  tag:'span',
15002     //                                  cls: 'fc-event-time',
15003     //                                  html : cells.length > 1 ? '' : ev.time
15004     //                                },
15005                                     {
15006                                       tag:'span',
15007                                       cls: 'fc-event-title',
15008                                       html : String.format('{0}', ev.title)
15009                                     }
15010
15011
15012                                 ]
15013                             },
15014                             {
15015                                 cls: 'ui-resizable-handle ui-resizable-e',
15016                                 html : '&nbsp;&nbsp;&nbsp'
15017                             }
15018
15019                         ]
15020                     };
15021
15022                     if (i == 0) {
15023                         cfg.cls += ' fc-event-start';
15024                     }
15025                     if ((i+1) == rows.length) {
15026                         cfg.cls += ' fc-event-end';
15027                     }
15028
15029                     var ctr = _this.el.select('.fc-event-container',true).first();
15030                     var cg = ctr.createChild(cfg);
15031
15032                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15033                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15034
15035                     var r = (c.more.length) ? 1 : 0;
15036                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15037                     cg.setWidth(ebox.right - sbox.x -2);
15038
15039                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15040                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15041                     cg.on('click', _this.onEventClick, _this, ev);
15042
15043                     ev.els.push(cg);
15044                     
15045                 }
15046                 
15047             }
15048             
15049             
15050             if(c.more.length){
15051                 var  cfg = {
15052                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15053                     style : 'position: absolute',
15054                     unselectable : "on",
15055                     cn : [
15056                         {
15057                             cls: 'fc-event-inner',
15058                             cn : [
15059                                 {
15060                                   tag:'span',
15061                                   cls: 'fc-event-title',
15062                                   html : 'More'
15063                                 }
15064
15065
15066                             ]
15067                         },
15068                         {
15069                             cls: 'ui-resizable-handle ui-resizable-e',
15070                             html : '&nbsp;&nbsp;&nbsp'
15071                         }
15072
15073                     ]
15074                 };
15075
15076                 var ctr = _this.el.select('.fc-event-container',true).first();
15077                 var cg = ctr.createChild(cfg);
15078
15079                 var sbox = c.select('.fc-day-content',true).first().getBox();
15080                 var ebox = c.select('.fc-day-content',true).first().getBox();
15081                 //Roo.log(cg);
15082                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15083                 cg.setWidth(ebox.right - sbox.x -2);
15084
15085                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15086                 
15087             }
15088             
15089         });
15090         
15091         
15092         
15093     },
15094     
15095     onEventEnter: function (e, el,event,d) {
15096         this.fireEvent('evententer', this, el, event);
15097     },
15098     
15099     onEventLeave: function (e, el,event,d) {
15100         this.fireEvent('eventleave', this, el, event);
15101     },
15102     
15103     onEventClick: function (e, el,event,d) {
15104         this.fireEvent('eventclick', this, el, event);
15105     },
15106     
15107     onMonthChange: function () {
15108         this.store.load();
15109     },
15110     
15111     onMoreEventClick: function(e, el, more)
15112     {
15113         var _this = this;
15114         
15115         this.calpopover.placement = 'right';
15116         this.calpopover.setTitle('More');
15117         
15118         this.calpopover.setContent('');
15119         
15120         var ctr = this.calpopover.el.select('.popover-content', true).first();
15121         
15122         Roo.each(more, function(m){
15123             var cfg = {
15124                 cls : 'fc-event-hori fc-event-draggable',
15125                 html : m.title
15126             }
15127             var cg = ctr.createChild(cfg);
15128             
15129             cg.on('click', _this.onEventClick, _this, m);
15130         });
15131         
15132         this.calpopover.show(el);
15133         
15134         
15135     },
15136     
15137     onLoad: function () 
15138     {   
15139         this.calevents = [];
15140         var cal = this;
15141         
15142         if(this.store.getCount() > 0){
15143             this.store.data.each(function(d){
15144                cal.addItem({
15145                     id : d.data.id,
15146                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15147                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15148                     time : d.data.start_time,
15149                     title : d.data.title,
15150                     description : d.data.description,
15151                     venue : d.data.venue
15152                 });
15153             });
15154         }
15155         
15156         this.renderEvents();
15157         
15158         if(this.calevents.length && this.loadMask){
15159             this.maskEl.hide();
15160         }
15161     },
15162     
15163     onBeforeLoad: function()
15164     {
15165         this.clearEvents();
15166         if(this.loadMask){
15167             this.maskEl.show();
15168         }
15169     }
15170 });
15171
15172  
15173  /*
15174  * - LGPL
15175  *
15176  * element
15177  * 
15178  */
15179
15180 /**
15181  * @class Roo.bootstrap.Popover
15182  * @extends Roo.bootstrap.Component
15183  * Bootstrap Popover class
15184  * @cfg {String} html contents of the popover   (or false to use children..)
15185  * @cfg {String} title of popover (or false to hide)
15186  * @cfg {String} placement how it is placed
15187  * @cfg {String} trigger click || hover (or false to trigger manually)
15188  * @cfg {String} over what (parent or false to trigger manually.)
15189  * @cfg {Number} delay - delay before showing
15190  
15191  * @constructor
15192  * Create a new Popover
15193  * @param {Object} config The config object
15194  */
15195
15196 Roo.bootstrap.Popover = function(config){
15197     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15198 };
15199
15200 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15201     
15202     title: 'Fill in a title',
15203     html: false,
15204     
15205     placement : 'right',
15206     trigger : 'hover', // hover
15207     
15208     delay : 0,
15209     
15210     over: 'parent',
15211     
15212     can_build_overlaid : false,
15213     
15214     getChildContainer : function()
15215     {
15216         return this.el.select('.popover-content',true).first();
15217     },
15218     
15219     getAutoCreate : function(){
15220          Roo.log('make popover?');
15221         var cfg = {
15222            cls : 'popover roo-dynamic',
15223            style: 'display:block',
15224            cn : [
15225                 {
15226                     cls : 'arrow'
15227                 },
15228                 {
15229                     cls : 'popover-inner',
15230                     cn : [
15231                         {
15232                             tag: 'h3',
15233                             cls: 'popover-title',
15234                             html : this.title
15235                         },
15236                         {
15237                             cls : 'popover-content',
15238                             html : this.html
15239                         }
15240                     ]
15241                     
15242                 }
15243            ]
15244         };
15245         
15246         return cfg;
15247     },
15248     setTitle: function(str)
15249     {
15250         this.title = str;
15251         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15252     },
15253     setContent: function(str)
15254     {
15255         this.html = str;
15256         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15257     },
15258     // as it get's added to the bottom of the page.
15259     onRender : function(ct, position)
15260     {
15261         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15262         if(!this.el){
15263             var cfg = Roo.apply({},  this.getAutoCreate());
15264             cfg.id = Roo.id();
15265             
15266             if (this.cls) {
15267                 cfg.cls += ' ' + this.cls;
15268             }
15269             if (this.style) {
15270                 cfg.style = this.style;
15271             }
15272             Roo.log("adding to ")
15273             this.el = Roo.get(document.body).createChild(cfg, position);
15274             Roo.log(this.el);
15275         }
15276         this.initEvents();
15277     },
15278     
15279     initEvents : function()
15280     {
15281         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15282         this.el.enableDisplayMode('block');
15283         this.el.hide();
15284         if (this.over === false) {
15285             return; 
15286         }
15287         if (this.triggers === false) {
15288             return;
15289         }
15290         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15291         var triggers = this.trigger ? this.trigger.split(' ') : [];
15292         Roo.each(triggers, function(trigger) {
15293         
15294             if (trigger == 'click') {
15295                 on_el.on('click', this.toggle, this);
15296             } else if (trigger != 'manual') {
15297                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15298                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15299       
15300                 on_el.on(eventIn  ,this.enter, this);
15301                 on_el.on(eventOut, this.leave, this);
15302             }
15303         }, this);
15304         
15305     },
15306     
15307     
15308     // private
15309     timeout : null,
15310     hoverState : null,
15311     
15312     toggle : function () {
15313         this.hoverState == 'in' ? this.leave() : this.enter();
15314     },
15315     
15316     enter : function () {
15317        
15318     
15319         clearTimeout(this.timeout);
15320     
15321         this.hoverState = 'in';
15322     
15323         if (!this.delay || !this.delay.show) {
15324             this.show();
15325             return;
15326         }
15327         var _t = this;
15328         this.timeout = setTimeout(function () {
15329             if (_t.hoverState == 'in') {
15330                 _t.show();
15331             }
15332         }, this.delay.show)
15333     },
15334     leave : function() {
15335         clearTimeout(this.timeout);
15336     
15337         this.hoverState = 'out';
15338     
15339         if (!this.delay || !this.delay.hide) {
15340             this.hide();
15341             return;
15342         }
15343         var _t = this;
15344         this.timeout = setTimeout(function () {
15345             if (_t.hoverState == 'out') {
15346                 _t.hide();
15347             }
15348         }, this.delay.hide)
15349     },
15350     
15351     show : function (on_el)
15352     {
15353         if (!on_el) {
15354             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15355         }
15356         // set content.
15357         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15358         if (this.html !== false) {
15359             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15360         }
15361         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15362         if (!this.title.length) {
15363             this.el.select('.popover-title',true).hide();
15364         }
15365         
15366         var placement = typeof this.placement == 'function' ?
15367             this.placement.call(this, this.el, on_el) :
15368             this.placement;
15369             
15370         var autoToken = /\s?auto?\s?/i;
15371         var autoPlace = autoToken.test(placement);
15372         if (autoPlace) {
15373             placement = placement.replace(autoToken, '') || 'top';
15374         }
15375         
15376         //this.el.detach()
15377         //this.el.setXY([0,0]);
15378         this.el.show();
15379         this.el.dom.style.display='block';
15380         this.el.addClass(placement);
15381         
15382         //this.el.appendTo(on_el);
15383         
15384         var p = this.getPosition();
15385         var box = this.el.getBox();
15386         
15387         if (autoPlace) {
15388             // fixme..
15389         }
15390         var align = Roo.bootstrap.Popover.alignment[placement];
15391         this.el.alignTo(on_el, align[0],align[1]);
15392         //var arrow = this.el.select('.arrow',true).first();
15393         //arrow.set(align[2], 
15394         
15395         this.el.addClass('in');
15396         
15397         
15398         if (this.el.hasClass('fade')) {
15399             // fade it?
15400         }
15401         
15402     },
15403     hide : function()
15404     {
15405         this.el.setXY([0,0]);
15406         this.el.removeClass('in');
15407         this.el.hide();
15408         this.hoverState = null;
15409         
15410     }
15411     
15412 });
15413
15414 Roo.bootstrap.Popover.alignment = {
15415     'left' : ['r-l', [-10,0], 'right'],
15416     'right' : ['l-r', [10,0], 'left'],
15417     'bottom' : ['t-b', [0,10], 'top'],
15418     'top' : [ 'b-t', [0,-10], 'bottom']
15419 };
15420
15421  /*
15422  * - LGPL
15423  *
15424  * Progress
15425  * 
15426  */
15427
15428 /**
15429  * @class Roo.bootstrap.Progress
15430  * @extends Roo.bootstrap.Component
15431  * Bootstrap Progress class
15432  * @cfg {Boolean} striped striped of the progress bar
15433  * @cfg {Boolean} active animated of the progress bar
15434  * 
15435  * 
15436  * @constructor
15437  * Create a new Progress
15438  * @param {Object} config The config object
15439  */
15440
15441 Roo.bootstrap.Progress = function(config){
15442     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15443 };
15444
15445 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15446     
15447     striped : false,
15448     active: false,
15449     
15450     getAutoCreate : function(){
15451         var cfg = {
15452             tag: 'div',
15453             cls: 'progress'
15454         };
15455         
15456         
15457         if(this.striped){
15458             cfg.cls += ' progress-striped';
15459         }
15460       
15461         if(this.active){
15462             cfg.cls += ' active';
15463         }
15464         
15465         
15466         return cfg;
15467     }
15468    
15469 });
15470
15471  
15472
15473  /*
15474  * - LGPL
15475  *
15476  * ProgressBar
15477  * 
15478  */
15479
15480 /**
15481  * @class Roo.bootstrap.ProgressBar
15482  * @extends Roo.bootstrap.Component
15483  * Bootstrap ProgressBar class
15484  * @cfg {Number} aria_valuenow aria-value now
15485  * @cfg {Number} aria_valuemin aria-value min
15486  * @cfg {Number} aria_valuemax aria-value max
15487  * @cfg {String} label label for the progress bar
15488  * @cfg {String} panel (success | info | warning | danger )
15489  * @cfg {String} role role of the progress bar
15490  * @cfg {String} sr_only text
15491  * 
15492  * 
15493  * @constructor
15494  * Create a new ProgressBar
15495  * @param {Object} config The config object
15496  */
15497
15498 Roo.bootstrap.ProgressBar = function(config){
15499     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15500 };
15501
15502 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15503     
15504     aria_valuenow : 0,
15505     aria_valuemin : 0,
15506     aria_valuemax : 100,
15507     label : false,
15508     panel : false,
15509     role : false,
15510     sr_only: false,
15511     
15512     getAutoCreate : function()
15513     {
15514         
15515         var cfg = {
15516             tag: 'div',
15517             cls: 'progress-bar',
15518             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15519         };
15520         
15521         if(this.sr_only){
15522             cfg.cn = {
15523                 tag: 'span',
15524                 cls: 'sr-only',
15525                 html: this.sr_only
15526             }
15527         }
15528         
15529         if(this.role){
15530             cfg.role = this.role;
15531         }
15532         
15533         if(this.aria_valuenow){
15534             cfg['aria-valuenow'] = this.aria_valuenow;
15535         }
15536         
15537         if(this.aria_valuemin){
15538             cfg['aria-valuemin'] = this.aria_valuemin;
15539         }
15540         
15541         if(this.aria_valuemax){
15542             cfg['aria-valuemax'] = this.aria_valuemax;
15543         }
15544         
15545         if(this.label && !this.sr_only){
15546             cfg.html = this.label;
15547         }
15548         
15549         if(this.panel){
15550             cfg.cls += ' progress-bar-' + this.panel;
15551         }
15552         
15553         return cfg;
15554     },
15555     
15556     update : function(aria_valuenow)
15557     {
15558         this.aria_valuenow = aria_valuenow;
15559         
15560         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15561     }
15562    
15563 });
15564
15565  
15566
15567  /*
15568  * - LGPL
15569  *
15570  * column
15571  * 
15572  */
15573
15574 /**
15575  * @class Roo.bootstrap.TabGroup
15576  * @extends Roo.bootstrap.Column
15577  * Bootstrap Column class
15578  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15579  * @cfg {Boolean} carousel true to make the group behave like a carousel
15580  * @cfg {Number} bullets show the panel pointer.. default 0
15581  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15582  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15583  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15584  * 
15585  * @constructor
15586  * Create a new TabGroup
15587  * @param {Object} config The config object
15588  */
15589
15590 Roo.bootstrap.TabGroup = function(config){
15591     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15592     if (!this.navId) {
15593         this.navId = Roo.id();
15594     }
15595     this.tabs = [];
15596     Roo.bootstrap.TabGroup.register(this);
15597     
15598 };
15599
15600 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15601     
15602     carousel : false,
15603     transition : false,
15604     bullets : 0,
15605     timer : 0,
15606     autoslide : false,
15607     slideFn : false,
15608     slideOnTouch : false,
15609     
15610     getAutoCreate : function()
15611     {
15612         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15613         
15614         cfg.cls += ' tab-content';
15615         
15616         Roo.log('get auto create...............');
15617         
15618         if (this.carousel) {
15619             cfg.cls += ' carousel slide';
15620             
15621             cfg.cn = [{
15622                cls : 'carousel-inner'
15623             }];
15624         
15625             if(this.bullets > 0 && !Roo.isTouch){
15626                 
15627                 var bullets = {
15628                     cls : 'carousel-bullets',
15629                     cn : []
15630                 };
15631                 
15632                 if(this.bullets_cls){
15633                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15634                 }
15635                 
15636                 for (var i = 0; i < this.bullets; i++){
15637                     bullets.cn.push({
15638                         cls : 'bullet bullet-' + i
15639                     });
15640                 }
15641                 
15642                 bullets.cn.push({
15643                     cls : 'clear'
15644                 });
15645                 
15646                 cfg.cn[0].cn = bullets;
15647             }
15648         }
15649         
15650         return cfg;
15651     },
15652     
15653     initEvents:  function()
15654     {
15655         Roo.log('-------- init events on tab group ---------');
15656         
15657         if(this.bullets > 0 && !Roo.isTouch){
15658             this.initBullet();
15659         }
15660         
15661         Roo.log(this);
15662         
15663         if(Roo.isTouch && this.slideOnTouch){
15664             this.el.on("touchstart", this.onTouchStart, this);
15665         }
15666         
15667         if(this.autoslide){
15668             var _this = this;
15669             
15670             this.slideFn = window.setInterval(function() {
15671                 _this.showPanelNext();
15672             }, this.timer);
15673         }
15674         
15675     },
15676     
15677     onTouchStart : function(e, el, o)
15678     {
15679         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15680             return;
15681         }
15682         
15683         this.showPanelNext();
15684     },
15685     
15686     getChildContainer : function()
15687     {
15688         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15689     },
15690     
15691     /**
15692     * register a Navigation item
15693     * @param {Roo.bootstrap.NavItem} the navitem to add
15694     */
15695     register : function(item)
15696     {
15697         this.tabs.push( item);
15698         item.navId = this.navId; // not really needed..
15699     
15700     },
15701     
15702     getActivePanel : function()
15703     {
15704         var r = false;
15705         Roo.each(this.tabs, function(t) {
15706             if (t.active) {
15707                 r = t;
15708                 return false;
15709             }
15710             return null;
15711         });
15712         return r;
15713         
15714     },
15715     getPanelByName : function(n)
15716     {
15717         var r = false;
15718         Roo.each(this.tabs, function(t) {
15719             if (t.tabId == n) {
15720                 r = t;
15721                 return false;
15722             }
15723             return null;
15724         });
15725         return r;
15726     },
15727     indexOfPanel : function(p)
15728     {
15729         var r = false;
15730         Roo.each(this.tabs, function(t,i) {
15731             if (t.tabId == p.tabId) {
15732                 r = i;
15733                 return false;
15734             }
15735             return null;
15736         });
15737         return r;
15738     },
15739     /**
15740      * show a specific panel
15741      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15742      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15743      */
15744     showPanel : function (pan)
15745     {
15746         if(this.transition){
15747             Roo.log("waiting for the transitionend");
15748             return;
15749         }
15750         
15751         if (typeof(pan) == 'number') {
15752             pan = this.tabs[pan];
15753         }
15754         if (typeof(pan) == 'string') {
15755             pan = this.getPanelByName(pan);
15756         }
15757         if (pan.tabId == this.getActivePanel().tabId) {
15758             return true;
15759         }
15760         var cur = this.getActivePanel();
15761         
15762         if (false === cur.fireEvent('beforedeactivate')) {
15763             return false;
15764         }
15765         
15766         if(this.bullets > 0 && !Roo.isTouch){
15767             this.setActiveBullet(this.indexOfPanel(pan));
15768         }
15769         
15770         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15771             
15772             this.transition = true;
15773             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15774             var lr = dir == 'next' ? 'left' : 'right';
15775             pan.el.addClass(dir); // or prev
15776             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15777             cur.el.addClass(lr); // or right
15778             pan.el.addClass(lr);
15779             
15780             var _this = this;
15781             cur.el.on('transitionend', function() {
15782                 Roo.log("trans end?");
15783                 
15784                 pan.el.removeClass([lr,dir]);
15785                 pan.setActive(true);
15786                 
15787                 cur.el.removeClass([lr]);
15788                 cur.setActive(false);
15789                 
15790                 _this.transition = false;
15791                 
15792             }, this, { single:  true } );
15793             
15794             return true;
15795         }
15796         
15797         cur.setActive(false);
15798         pan.setActive(true);
15799         
15800         return true;
15801         
15802     },
15803     showPanelNext : function()
15804     {
15805         var i = this.indexOfPanel(this.getActivePanel());
15806         
15807         if (i >= this.tabs.length - 1 && !this.autoslide) {
15808             return;
15809         }
15810         
15811         if (i >= this.tabs.length - 1 && this.autoslide) {
15812             i = -1;
15813         }
15814         
15815         this.showPanel(this.tabs[i+1]);
15816     },
15817     
15818     showPanelPrev : function()
15819     {
15820         var i = this.indexOfPanel(this.getActivePanel());
15821         
15822         if (i  < 1 && !this.autoslide) {
15823             return;
15824         }
15825         
15826         if (i < 1 && this.autoslide) {
15827             i = this.tabs.length;
15828         }
15829         
15830         this.showPanel(this.tabs[i-1]);
15831     },
15832     
15833     initBullet : function()
15834     {
15835         if(Roo.isTouch){
15836             return;
15837         }
15838         
15839         var _this = this;
15840         
15841         for (var i = 0; i < this.bullets; i++){
15842             var bullet = this.el.select('.bullet-' + i, true).first();
15843
15844             if(!bullet){
15845                 continue;
15846             }
15847
15848             bullet.on('click', (function(e, el, o, ii, t){
15849
15850                 e.preventDefault();
15851
15852                 _this.showPanel(ii);
15853
15854                 if(_this.autoslide && _this.slideFn){
15855                     clearInterval(_this.slideFn);
15856                     _this.slideFn = window.setInterval(function() {
15857                         _this.showPanelNext();
15858                     }, _this.timer);
15859                 }
15860
15861             }).createDelegate(this, [i, bullet], true));
15862         }
15863     },
15864     
15865     setActiveBullet : function(i)
15866     {
15867         if(Roo.isTouch){
15868             return;
15869         }
15870         
15871         Roo.each(this.el.select('.bullet', true).elements, function(el){
15872             el.removeClass('selected');
15873         });
15874
15875         var bullet = this.el.select('.bullet-' + i, true).first();
15876         
15877         if(!bullet){
15878             return;
15879         }
15880         
15881         bullet.addClass('selected');
15882     }
15883     
15884     
15885   
15886 });
15887
15888  
15889
15890  
15891  
15892 Roo.apply(Roo.bootstrap.TabGroup, {
15893     
15894     groups: {},
15895      /**
15896     * register a Navigation Group
15897     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15898     */
15899     register : function(navgrp)
15900     {
15901         this.groups[navgrp.navId] = navgrp;
15902         
15903     },
15904     /**
15905     * fetch a Navigation Group based on the navigation ID
15906     * if one does not exist , it will get created.
15907     * @param {string} the navgroup to add
15908     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15909     */
15910     get: function(navId) {
15911         if (typeof(this.groups[navId]) == 'undefined') {
15912             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15913         }
15914         return this.groups[navId] ;
15915     }
15916     
15917     
15918     
15919 });
15920
15921  /*
15922  * - LGPL
15923  *
15924  * TabPanel
15925  * 
15926  */
15927
15928 /**
15929  * @class Roo.bootstrap.TabPanel
15930  * @extends Roo.bootstrap.Component
15931  * Bootstrap TabPanel class
15932  * @cfg {Boolean} active panel active
15933  * @cfg {String} html panel content
15934  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15935  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15936  * 
15937  * 
15938  * @constructor
15939  * Create a new TabPanel
15940  * @param {Object} config The config object
15941  */
15942
15943 Roo.bootstrap.TabPanel = function(config){
15944     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15945     this.addEvents({
15946         /**
15947              * @event changed
15948              * Fires when the active status changes
15949              * @param {Roo.bootstrap.TabPanel} this
15950              * @param {Boolean} state the new state
15951             
15952          */
15953         'changed': true,
15954         /**
15955              * @event beforedeactivate
15956              * Fires before a tab is de-activated - can be used to do validation on a form.
15957              * @param {Roo.bootstrap.TabPanel} this
15958              * @return {Boolean} false if there is an error
15959             
15960          */
15961         'beforedeactivate': true
15962      });
15963     
15964     this.tabId = this.tabId || Roo.id();
15965   
15966 };
15967
15968 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15969     
15970     active: false,
15971     html: false,
15972     tabId: false,
15973     navId : false,
15974     
15975     getAutoCreate : function(){
15976         var cfg = {
15977             tag: 'div',
15978             // item is needed for carousel - not sure if it has any effect otherwise
15979             cls: 'tab-pane item',
15980             html: this.html || ''
15981         };
15982         
15983         if(this.active){
15984             cfg.cls += ' active';
15985         }
15986         
15987         if(this.tabId){
15988             cfg.tabId = this.tabId;
15989         }
15990         
15991         
15992         return cfg;
15993     },
15994     
15995     initEvents:  function()
15996     {
15997         Roo.log('-------- init events on tab panel ---------');
15998         
15999         var p = this.parent();
16000         this.navId = this.navId || p.navId;
16001         
16002         if (typeof(this.navId) != 'undefined') {
16003             // not really needed.. but just in case.. parent should be a NavGroup.
16004             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16005             Roo.log(['register', tg, this]);
16006             tg.register(this);
16007             
16008             var i = tg.tabs.length - 1;
16009             
16010             if(this.active && tg.bullets > 0 && i < tg.bullets){
16011                 tg.setActiveBullet(i);
16012             }
16013         }
16014         
16015     },
16016     
16017     
16018     onRender : function(ct, position)
16019     {
16020        // Roo.log("Call onRender: " + this.xtype);
16021         
16022         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16023         
16024         
16025         
16026         
16027         
16028     },
16029     
16030     setActive: function(state)
16031     {
16032         Roo.log("panel - set active " + this.tabId + "=" + state);
16033         
16034         this.active = state;
16035         if (!state) {
16036             this.el.removeClass('active');
16037             
16038         } else  if (!this.el.hasClass('active')) {
16039             this.el.addClass('active');
16040         }
16041         
16042         this.fireEvent('changed', this, state);
16043     }
16044     
16045     
16046 });
16047  
16048
16049  
16050
16051  /*
16052  * - LGPL
16053  *
16054  * DateField
16055  * 
16056  */
16057
16058 /**
16059  * @class Roo.bootstrap.DateField
16060  * @extends Roo.bootstrap.Input
16061  * Bootstrap DateField class
16062  * @cfg {Number} weekStart default 0
16063  * @cfg {String} viewMode default empty, (months|years)
16064  * @cfg {String} minViewMode default empty, (months|years)
16065  * @cfg {Number} startDate default -Infinity
16066  * @cfg {Number} endDate default Infinity
16067  * @cfg {Boolean} todayHighlight default false
16068  * @cfg {Boolean} todayBtn default false
16069  * @cfg {Boolean} calendarWeeks default false
16070  * @cfg {Object} daysOfWeekDisabled default empty
16071  * @cfg {Boolean} singleMode default false (true | false)
16072  * 
16073  * @cfg {Boolean} keyboardNavigation default true
16074  * @cfg {String} language default en
16075  * 
16076  * @constructor
16077  * Create a new DateField
16078  * @param {Object} config The config object
16079  */
16080
16081 Roo.bootstrap.DateField = function(config){
16082     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16083      this.addEvents({
16084             /**
16085              * @event show
16086              * Fires when this field show.
16087              * @param {Roo.bootstrap.DateField} this
16088              * @param {Mixed} date The date value
16089              */
16090             show : true,
16091             /**
16092              * @event show
16093              * Fires when this field hide.
16094              * @param {Roo.bootstrap.DateField} this
16095              * @param {Mixed} date The date value
16096              */
16097             hide : true,
16098             /**
16099              * @event select
16100              * Fires when select a date.
16101              * @param {Roo.bootstrap.DateField} this
16102              * @param {Mixed} date The date value
16103              */
16104             select : true
16105         });
16106 };
16107
16108 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16109     
16110     /**
16111      * @cfg {String} format
16112      * The default date format string which can be overriden for localization support.  The format must be
16113      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16114      */
16115     format : "m/d/y",
16116     /**
16117      * @cfg {String} altFormats
16118      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16119      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16120      */
16121     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16122     
16123     weekStart : 0,
16124     
16125     viewMode : '',
16126     
16127     minViewMode : '',
16128     
16129     todayHighlight : false,
16130     
16131     todayBtn: false,
16132     
16133     language: 'en',
16134     
16135     keyboardNavigation: true,
16136     
16137     calendarWeeks: false,
16138     
16139     startDate: -Infinity,
16140     
16141     endDate: Infinity,
16142     
16143     daysOfWeekDisabled: [],
16144     
16145     _events: [],
16146     
16147     singleMode : false,
16148     
16149     UTCDate: function()
16150     {
16151         return new Date(Date.UTC.apply(Date, arguments));
16152     },
16153     
16154     UTCToday: function()
16155     {
16156         var today = new Date();
16157         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16158     },
16159     
16160     getDate: function() {
16161             var d = this.getUTCDate();
16162             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16163     },
16164     
16165     getUTCDate: function() {
16166             return this.date;
16167     },
16168     
16169     setDate: function(d) {
16170             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16171     },
16172     
16173     setUTCDate: function(d) {
16174             this.date = d;
16175             this.setValue(this.formatDate(this.date));
16176     },
16177         
16178     onRender: function(ct, position)
16179     {
16180         
16181         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16182         
16183         this.language = this.language || 'en';
16184         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16185         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16186         
16187         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16188         this.format = this.format || 'm/d/y';
16189         this.isInline = false;
16190         this.isInput = true;
16191         this.component = this.el.select('.add-on', true).first() || false;
16192         this.component = (this.component && this.component.length === 0) ? false : this.component;
16193         this.hasInput = this.component && this.inputEL().length;
16194         
16195         if (typeof(this.minViewMode === 'string')) {
16196             switch (this.minViewMode) {
16197                 case 'months':
16198                     this.minViewMode = 1;
16199                     break;
16200                 case 'years':
16201                     this.minViewMode = 2;
16202                     break;
16203                 default:
16204                     this.minViewMode = 0;
16205                     break;
16206             }
16207         }
16208         
16209         if (typeof(this.viewMode === 'string')) {
16210             switch (this.viewMode) {
16211                 case 'months':
16212                     this.viewMode = 1;
16213                     break;
16214                 case 'years':
16215                     this.viewMode = 2;
16216                     break;
16217                 default:
16218                     this.viewMode = 0;
16219                     break;
16220             }
16221         }
16222                 
16223         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16224         
16225 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16226         
16227         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16228         
16229         this.picker().on('mousedown', this.onMousedown, this);
16230         this.picker().on('click', this.onClick, this);
16231         
16232         this.picker().addClass('datepicker-dropdown');
16233         
16234         this.startViewMode = this.viewMode;
16235         
16236         if(this.singleMode){
16237             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16238                 v.setVisibilityMode(Roo.Element.DISPLAY)
16239                 v.hide();
16240             });
16241             
16242             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16243                 v.setStyle('width', '189px');
16244             });
16245         }
16246         
16247         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16248             if(!this.calendarWeeks){
16249                 v.remove();
16250                 return;
16251             }
16252             
16253             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16254             v.attr('colspan', function(i, val){
16255                 return parseInt(val) + 1;
16256             });
16257         })
16258                         
16259         
16260         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16261         
16262         this.setStartDate(this.startDate);
16263         this.setEndDate(this.endDate);
16264         
16265         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16266         
16267         this.fillDow();
16268         this.fillMonths();
16269         this.update();
16270         this.showMode();
16271         
16272         if(this.isInline) {
16273             this.show();
16274         }
16275     },
16276     
16277     picker : function()
16278     {
16279         return this.pickerEl;
16280 //        return this.el.select('.datepicker', true).first();
16281     },
16282     
16283     fillDow: function()
16284     {
16285         var dowCnt = this.weekStart;
16286         
16287         var dow = {
16288             tag: 'tr',
16289             cn: [
16290                 
16291             ]
16292         };
16293         
16294         if(this.calendarWeeks){
16295             dow.cn.push({
16296                 tag: 'th',
16297                 cls: 'cw',
16298                 html: '&nbsp;'
16299             })
16300         }
16301         
16302         while (dowCnt < this.weekStart + 7) {
16303             dow.cn.push({
16304                 tag: 'th',
16305                 cls: 'dow',
16306                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16307             });
16308         }
16309         
16310         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16311     },
16312     
16313     fillMonths: function()
16314     {    
16315         var i = 0;
16316         var months = this.picker().select('>.datepicker-months td', true).first();
16317         
16318         months.dom.innerHTML = '';
16319         
16320         while (i < 12) {
16321             var month = {
16322                 tag: 'span',
16323                 cls: 'month',
16324                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16325             }
16326             
16327             months.createChild(month);
16328         }
16329         
16330     },
16331     
16332     update: function()
16333     {
16334         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;
16335         
16336         if (this.date < this.startDate) {
16337             this.viewDate = new Date(this.startDate);
16338         } else if (this.date > this.endDate) {
16339             this.viewDate = new Date(this.endDate);
16340         } else {
16341             this.viewDate = new Date(this.date);
16342         }
16343         
16344         this.fill();
16345     },
16346     
16347     fill: function() 
16348     {
16349         var d = new Date(this.viewDate),
16350                 year = d.getUTCFullYear(),
16351                 month = d.getUTCMonth(),
16352                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16353                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16354                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16355                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16356                 currentDate = this.date && this.date.valueOf(),
16357                 today = this.UTCToday();
16358         
16359         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16360         
16361 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16362         
16363 //        this.picker.select('>tfoot th.today').
16364 //                                              .text(dates[this.language].today)
16365 //                                              .toggle(this.todayBtn !== false);
16366     
16367         this.updateNavArrows();
16368         this.fillMonths();
16369                                                 
16370         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16371         
16372         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16373          
16374         prevMonth.setUTCDate(day);
16375         
16376         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16377         
16378         var nextMonth = new Date(prevMonth);
16379         
16380         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16381         
16382         nextMonth = nextMonth.valueOf();
16383         
16384         var fillMonths = false;
16385         
16386         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16387         
16388         while(prevMonth.valueOf() < nextMonth) {
16389             var clsName = '';
16390             
16391             if (prevMonth.getUTCDay() === this.weekStart) {
16392                 if(fillMonths){
16393                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16394                 }
16395                     
16396                 fillMonths = {
16397                     tag: 'tr',
16398                     cn: []
16399                 };
16400                 
16401                 if(this.calendarWeeks){
16402                     // ISO 8601: First week contains first thursday.
16403                     // ISO also states week starts on Monday, but we can be more abstract here.
16404                     var
16405                     // Start of current week: based on weekstart/current date
16406                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16407                     // Thursday of this week
16408                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16409                     // First Thursday of year, year from thursday
16410                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16411                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16412                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16413                     
16414                     fillMonths.cn.push({
16415                         tag: 'td',
16416                         cls: 'cw',
16417                         html: calWeek
16418                     });
16419                 }
16420             }
16421             
16422             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16423                 clsName += ' old';
16424             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16425                 clsName += ' new';
16426             }
16427             if (this.todayHighlight &&
16428                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16429                 prevMonth.getUTCMonth() == today.getMonth() &&
16430                 prevMonth.getUTCDate() == today.getDate()) {
16431                 clsName += ' today';
16432             }
16433             
16434             if (currentDate && prevMonth.valueOf() === currentDate) {
16435                 clsName += ' active';
16436             }
16437             
16438             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16439                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16440                     clsName += ' disabled';
16441             }
16442             
16443             fillMonths.cn.push({
16444                 tag: 'td',
16445                 cls: 'day ' + clsName,
16446                 html: prevMonth.getDate()
16447             })
16448             
16449             prevMonth.setDate(prevMonth.getDate()+1);
16450         }
16451           
16452         var currentYear = this.date && this.date.getUTCFullYear();
16453         var currentMonth = this.date && this.date.getUTCMonth();
16454         
16455         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16456         
16457         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16458             v.removeClass('active');
16459             
16460             if(currentYear === year && k === currentMonth){
16461                 v.addClass('active');
16462             }
16463             
16464             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16465                 v.addClass('disabled');
16466             }
16467             
16468         });
16469         
16470         
16471         year = parseInt(year/10, 10) * 10;
16472         
16473         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16474         
16475         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16476         
16477         year -= 1;
16478         for (var i = -1; i < 11; i++) {
16479             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16480                 tag: 'span',
16481                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16482                 html: year
16483             })
16484             
16485             year += 1;
16486         }
16487     },
16488     
16489     showMode: function(dir) 
16490     {
16491         if (dir) {
16492             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16493         }
16494         
16495         Roo.each(this.picker().select('>div',true).elements, function(v){
16496             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16497             v.hide();
16498         });
16499         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16500     },
16501     
16502     place: function()
16503     {
16504         if(this.isInline) return;
16505         
16506         this.picker().removeClass(['bottom', 'top']);
16507         
16508         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16509             /*
16510              * place to the top of element!
16511              *
16512              */
16513             
16514             this.picker().addClass('top');
16515             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16516             
16517             return;
16518         }
16519         
16520         this.picker().addClass('bottom');
16521         
16522         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16523     },
16524     
16525     parseDate : function(value)
16526     {
16527         if(!value || value instanceof Date){
16528             return value;
16529         }
16530         var v = Date.parseDate(value, this.format);
16531         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16532             v = Date.parseDate(value, 'Y-m-d');
16533         }
16534         if(!v && this.altFormats){
16535             if(!this.altFormatsArray){
16536                 this.altFormatsArray = this.altFormats.split("|");
16537             }
16538             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16539                 v = Date.parseDate(value, this.altFormatsArray[i]);
16540             }
16541         }
16542         return v;
16543     },
16544     
16545     formatDate : function(date, fmt)
16546     {   
16547         return (!date || !(date instanceof Date)) ?
16548         date : date.dateFormat(fmt || this.format);
16549     },
16550     
16551     onFocus : function()
16552     {
16553         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16554         this.show();
16555     },
16556     
16557     onBlur : function()
16558     {
16559         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16560         
16561         var d = this.inputEl().getValue();
16562         
16563         this.setValue(d);
16564                 
16565         this.hide();
16566     },
16567     
16568     show : function()
16569     {
16570         this.picker().show();
16571         this.update();
16572         this.place();
16573         
16574         this.fireEvent('show', this, this.date);
16575     },
16576     
16577     hide : function()
16578     {
16579         if(this.isInline) return;
16580         this.picker().hide();
16581         this.viewMode = this.startViewMode;
16582         this.showMode();
16583         
16584         this.fireEvent('hide', this, this.date);
16585         
16586     },
16587     
16588     onMousedown: function(e)
16589     {
16590         e.stopPropagation();
16591         e.preventDefault();
16592     },
16593     
16594     keyup: function(e)
16595     {
16596         Roo.bootstrap.DateField.superclass.keyup.call(this);
16597         this.update();
16598     },
16599
16600     setValue: function(v)
16601     {
16602         
16603         // v can be a string or a date..
16604         
16605         
16606         var d = new Date(this.parseDate(v) ).clearTime();
16607         
16608         if(isNaN(d.getTime())){
16609             this.date = this.viewDate = '';
16610             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16611             return;
16612         }
16613         
16614         v = this.formatDate(d);
16615         
16616         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16617         
16618         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16619      
16620         this.update();
16621
16622         this.fireEvent('select', this, this.date);
16623         
16624     },
16625     
16626     getValue: function()
16627     {
16628         return this.formatDate(this.date);
16629     },
16630     
16631     fireKey: function(e)
16632     {
16633         if (!this.picker().isVisible()){
16634             if (e.keyCode == 27) // allow escape to hide and re-show picker
16635                 this.show();
16636             return;
16637         }
16638         
16639         var dateChanged = false,
16640         dir, day, month,
16641         newDate, newViewDate;
16642         
16643         switch(e.keyCode){
16644             case 27: // escape
16645                 this.hide();
16646                 e.preventDefault();
16647                 break;
16648             case 37: // left
16649             case 39: // right
16650                 if (!this.keyboardNavigation) break;
16651                 dir = e.keyCode == 37 ? -1 : 1;
16652                 
16653                 if (e.ctrlKey){
16654                     newDate = this.moveYear(this.date, dir);
16655                     newViewDate = this.moveYear(this.viewDate, dir);
16656                 } else if (e.shiftKey){
16657                     newDate = this.moveMonth(this.date, dir);
16658                     newViewDate = this.moveMonth(this.viewDate, dir);
16659                 } else {
16660                     newDate = new Date(this.date);
16661                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16662                     newViewDate = new Date(this.viewDate);
16663                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16664                 }
16665                 if (this.dateWithinRange(newDate)){
16666                     this.date = newDate;
16667                     this.viewDate = newViewDate;
16668                     this.setValue(this.formatDate(this.date));
16669 //                    this.update();
16670                     e.preventDefault();
16671                     dateChanged = true;
16672                 }
16673                 break;
16674             case 38: // up
16675             case 40: // down
16676                 if (!this.keyboardNavigation) break;
16677                 dir = e.keyCode == 38 ? -1 : 1;
16678                 if (e.ctrlKey){
16679                     newDate = this.moveYear(this.date, dir);
16680                     newViewDate = this.moveYear(this.viewDate, dir);
16681                 } else if (e.shiftKey){
16682                     newDate = this.moveMonth(this.date, dir);
16683                     newViewDate = this.moveMonth(this.viewDate, dir);
16684                 } else {
16685                     newDate = new Date(this.date);
16686                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16687                     newViewDate = new Date(this.viewDate);
16688                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16689                 }
16690                 if (this.dateWithinRange(newDate)){
16691                     this.date = newDate;
16692                     this.viewDate = newViewDate;
16693                     this.setValue(this.formatDate(this.date));
16694 //                    this.update();
16695                     e.preventDefault();
16696                     dateChanged = true;
16697                 }
16698                 break;
16699             case 13: // enter
16700                 this.setValue(this.formatDate(this.date));
16701                 this.hide();
16702                 e.preventDefault();
16703                 break;
16704             case 9: // tab
16705                 this.setValue(this.formatDate(this.date));
16706                 this.hide();
16707                 break;
16708             case 16: // shift
16709             case 17: // ctrl
16710             case 18: // alt
16711                 break;
16712             default :
16713                 this.hide();
16714                 
16715         }
16716     },
16717     
16718     
16719     onClick: function(e) 
16720     {
16721         e.stopPropagation();
16722         e.preventDefault();
16723         
16724         var target = e.getTarget();
16725         
16726         if(target.nodeName.toLowerCase() === 'i'){
16727             target = Roo.get(target).dom.parentNode;
16728         }
16729         
16730         var nodeName = target.nodeName;
16731         var className = target.className;
16732         var html = target.innerHTML;
16733         //Roo.log(nodeName);
16734         
16735         switch(nodeName.toLowerCase()) {
16736             case 'th':
16737                 switch(className) {
16738                     case 'switch':
16739                         this.showMode(1);
16740                         break;
16741                     case 'prev':
16742                     case 'next':
16743                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16744                         switch(this.viewMode){
16745                                 case 0:
16746                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16747                                         break;
16748                                 case 1:
16749                                 case 2:
16750                                         this.viewDate = this.moveYear(this.viewDate, dir);
16751                                         break;
16752                         }
16753                         this.fill();
16754                         break;
16755                     case 'today':
16756                         var date = new Date();
16757                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16758 //                        this.fill()
16759                         this.setValue(this.formatDate(this.date));
16760                         
16761                         this.hide();
16762                         break;
16763                 }
16764                 break;
16765             case 'span':
16766                 if (className.indexOf('disabled') < 0) {
16767                     this.viewDate.setUTCDate(1);
16768                     if (className.indexOf('month') > -1) {
16769                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16770                     } else {
16771                         var year = parseInt(html, 10) || 0;
16772                         this.viewDate.setUTCFullYear(year);
16773                         
16774                     }
16775                     
16776                     if(this.singleMode){
16777                         this.setValue(this.formatDate(this.viewDate));
16778                         this.hide();
16779                         return;
16780                     }
16781                     
16782                     this.showMode(-1);
16783                     this.fill();
16784                 }
16785                 break;
16786                 
16787             case 'td':
16788                 //Roo.log(className);
16789                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16790                     var day = parseInt(html, 10) || 1;
16791                     var year = this.viewDate.getUTCFullYear(),
16792                         month = this.viewDate.getUTCMonth();
16793
16794                     if (className.indexOf('old') > -1) {
16795                         if(month === 0 ){
16796                             month = 11;
16797                             year -= 1;
16798                         }else{
16799                             month -= 1;
16800                         }
16801                     } else if (className.indexOf('new') > -1) {
16802                         if (month == 11) {
16803                             month = 0;
16804                             year += 1;
16805                         } else {
16806                             month += 1;
16807                         }
16808                     }
16809                     //Roo.log([year,month,day]);
16810                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16811                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16812 //                    this.fill();
16813                     //Roo.log(this.formatDate(this.date));
16814                     this.setValue(this.formatDate(this.date));
16815                     this.hide();
16816                 }
16817                 break;
16818         }
16819     },
16820     
16821     setStartDate: function(startDate)
16822     {
16823         this.startDate = startDate || -Infinity;
16824         if (this.startDate !== -Infinity) {
16825             this.startDate = this.parseDate(this.startDate);
16826         }
16827         this.update();
16828         this.updateNavArrows();
16829     },
16830
16831     setEndDate: function(endDate)
16832     {
16833         this.endDate = endDate || Infinity;
16834         if (this.endDate !== Infinity) {
16835             this.endDate = this.parseDate(this.endDate);
16836         }
16837         this.update();
16838         this.updateNavArrows();
16839     },
16840     
16841     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16842     {
16843         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16844         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16845             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16846         }
16847         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16848             return parseInt(d, 10);
16849         });
16850         this.update();
16851         this.updateNavArrows();
16852     },
16853     
16854     updateNavArrows: function() 
16855     {
16856         if(this.singleMode){
16857             return;
16858         }
16859         
16860         var d = new Date(this.viewDate),
16861         year = d.getUTCFullYear(),
16862         month = d.getUTCMonth();
16863         
16864         Roo.each(this.picker().select('.prev', true).elements, function(v){
16865             v.show();
16866             switch (this.viewMode) {
16867                 case 0:
16868
16869                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16870                         v.hide();
16871                     }
16872                     break;
16873                 case 1:
16874                 case 2:
16875                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16876                         v.hide();
16877                     }
16878                     break;
16879             }
16880         });
16881         
16882         Roo.each(this.picker().select('.next', true).elements, function(v){
16883             v.show();
16884             switch (this.viewMode) {
16885                 case 0:
16886
16887                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16888                         v.hide();
16889                     }
16890                     break;
16891                 case 1:
16892                 case 2:
16893                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16894                         v.hide();
16895                     }
16896                     break;
16897             }
16898         })
16899     },
16900     
16901     moveMonth: function(date, dir)
16902     {
16903         if (!dir) return date;
16904         var new_date = new Date(date.valueOf()),
16905         day = new_date.getUTCDate(),
16906         month = new_date.getUTCMonth(),
16907         mag = Math.abs(dir),
16908         new_month, test;
16909         dir = dir > 0 ? 1 : -1;
16910         if (mag == 1){
16911             test = dir == -1
16912             // If going back one month, make sure month is not current month
16913             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16914             ? function(){
16915                 return new_date.getUTCMonth() == month;
16916             }
16917             // If going forward one month, make sure month is as expected
16918             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16919             : function(){
16920                 return new_date.getUTCMonth() != new_month;
16921             };
16922             new_month = month + dir;
16923             new_date.setUTCMonth(new_month);
16924             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16925             if (new_month < 0 || new_month > 11)
16926                 new_month = (new_month + 12) % 12;
16927         } else {
16928             // For magnitudes >1, move one month at a time...
16929             for (var i=0; i<mag; i++)
16930                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16931                 new_date = this.moveMonth(new_date, dir);
16932             // ...then reset the day, keeping it in the new month
16933             new_month = new_date.getUTCMonth();
16934             new_date.setUTCDate(day);
16935             test = function(){
16936                 return new_month != new_date.getUTCMonth();
16937             };
16938         }
16939         // Common date-resetting loop -- if date is beyond end of month, make it
16940         // end of month
16941         while (test()){
16942             new_date.setUTCDate(--day);
16943             new_date.setUTCMonth(new_month);
16944         }
16945         return new_date;
16946     },
16947
16948     moveYear: function(date, dir)
16949     {
16950         return this.moveMonth(date, dir*12);
16951     },
16952
16953     dateWithinRange: function(date)
16954     {
16955         return date >= this.startDate && date <= this.endDate;
16956     },
16957
16958     
16959     remove: function() 
16960     {
16961         this.picker().remove();
16962     }
16963    
16964 });
16965
16966 Roo.apply(Roo.bootstrap.DateField,  {
16967     
16968     head : {
16969         tag: 'thead',
16970         cn: [
16971         {
16972             tag: 'tr',
16973             cn: [
16974             {
16975                 tag: 'th',
16976                 cls: 'prev',
16977                 html: '<i class="fa fa-arrow-left"/>'
16978             },
16979             {
16980                 tag: 'th',
16981                 cls: 'switch',
16982                 colspan: '5'
16983             },
16984             {
16985                 tag: 'th',
16986                 cls: 'next',
16987                 html: '<i class="fa fa-arrow-right"/>'
16988             }
16989
16990             ]
16991         }
16992         ]
16993     },
16994     
16995     content : {
16996         tag: 'tbody',
16997         cn: [
16998         {
16999             tag: 'tr',
17000             cn: [
17001             {
17002                 tag: 'td',
17003                 colspan: '7'
17004             }
17005             ]
17006         }
17007         ]
17008     },
17009     
17010     footer : {
17011         tag: 'tfoot',
17012         cn: [
17013         {
17014             tag: 'tr',
17015             cn: [
17016             {
17017                 tag: 'th',
17018                 colspan: '7',
17019                 cls: 'today'
17020             }
17021                     
17022             ]
17023         }
17024         ]
17025     },
17026     
17027     dates:{
17028         en: {
17029             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17030             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17031             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17032             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17033             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17034             today: "Today"
17035         }
17036     },
17037     
17038     modes: [
17039     {
17040         clsName: 'days',
17041         navFnc: 'Month',
17042         navStep: 1
17043     },
17044     {
17045         clsName: 'months',
17046         navFnc: 'FullYear',
17047         navStep: 1
17048     },
17049     {
17050         clsName: 'years',
17051         navFnc: 'FullYear',
17052         navStep: 10
17053     }]
17054 });
17055
17056 Roo.apply(Roo.bootstrap.DateField,  {
17057   
17058     template : {
17059         tag: 'div',
17060         cls: 'datepicker dropdown-menu roo-dynamic',
17061         cn: [
17062         {
17063             tag: 'div',
17064             cls: 'datepicker-days',
17065             cn: [
17066             {
17067                 tag: 'table',
17068                 cls: 'table-condensed',
17069                 cn:[
17070                 Roo.bootstrap.DateField.head,
17071                 {
17072                     tag: 'tbody'
17073                 },
17074                 Roo.bootstrap.DateField.footer
17075                 ]
17076             }
17077             ]
17078         },
17079         {
17080             tag: 'div',
17081             cls: 'datepicker-months',
17082             cn: [
17083             {
17084                 tag: 'table',
17085                 cls: 'table-condensed',
17086                 cn:[
17087                 Roo.bootstrap.DateField.head,
17088                 Roo.bootstrap.DateField.content,
17089                 Roo.bootstrap.DateField.footer
17090                 ]
17091             }
17092             ]
17093         },
17094         {
17095             tag: 'div',
17096             cls: 'datepicker-years',
17097             cn: [
17098             {
17099                 tag: 'table',
17100                 cls: 'table-condensed',
17101                 cn:[
17102                 Roo.bootstrap.DateField.head,
17103                 Roo.bootstrap.DateField.content,
17104                 Roo.bootstrap.DateField.footer
17105                 ]
17106             }
17107             ]
17108         }
17109         ]
17110     }
17111 });
17112
17113  
17114
17115  /*
17116  * - LGPL
17117  *
17118  * TimeField
17119  * 
17120  */
17121
17122 /**
17123  * @class Roo.bootstrap.TimeField
17124  * @extends Roo.bootstrap.Input
17125  * Bootstrap DateField class
17126  * 
17127  * 
17128  * @constructor
17129  * Create a new TimeField
17130  * @param {Object} config The config object
17131  */
17132
17133 Roo.bootstrap.TimeField = function(config){
17134     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17135     this.addEvents({
17136             /**
17137              * @event show
17138              * Fires when this field show.
17139              * @param {Roo.bootstrap.DateField} thisthis
17140              * @param {Mixed} date The date value
17141              */
17142             show : true,
17143             /**
17144              * @event show
17145              * Fires when this field hide.
17146              * @param {Roo.bootstrap.DateField} this
17147              * @param {Mixed} date The date value
17148              */
17149             hide : true,
17150             /**
17151              * @event select
17152              * Fires when select a date.
17153              * @param {Roo.bootstrap.DateField} this
17154              * @param {Mixed} date The date value
17155              */
17156             select : true
17157         });
17158 };
17159
17160 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17161     
17162     /**
17163      * @cfg {String} format
17164      * The default time format string which can be overriden for localization support.  The format must be
17165      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17166      */
17167     format : "H:i",
17168        
17169     onRender: function(ct, position)
17170     {
17171         
17172         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17173                 
17174         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17175         
17176         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17177         
17178         this.pop = this.picker().select('>.datepicker-time',true).first();
17179         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17180         
17181         this.picker().on('mousedown', this.onMousedown, this);
17182         this.picker().on('click', this.onClick, this);
17183         
17184         this.picker().addClass('datepicker-dropdown');
17185     
17186         this.fillTime();
17187         this.update();
17188             
17189         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17190         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17191         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17192         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17193         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17194         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17195
17196     },
17197     
17198     fireKey: function(e){
17199         if (!this.picker().isVisible()){
17200             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17201                 this.show();
17202             }
17203             return;
17204         }
17205
17206         e.preventDefault();
17207         
17208         switch(e.keyCode){
17209             case 27: // escape
17210                 this.hide();
17211                 break;
17212             case 37: // left
17213             case 39: // right
17214                 this.onTogglePeriod();
17215                 break;
17216             case 38: // up
17217                 this.onIncrementMinutes();
17218                 break;
17219             case 40: // down
17220                 this.onDecrementMinutes();
17221                 break;
17222             case 13: // enter
17223             case 9: // tab
17224                 this.setTime();
17225                 break;
17226         }
17227     },
17228     
17229     onClick: function(e) {
17230         e.stopPropagation();
17231         e.preventDefault();
17232     },
17233     
17234     picker : function()
17235     {
17236         return this.el.select('.datepicker', true).first();
17237     },
17238     
17239     fillTime: function()
17240     {    
17241         var time = this.pop.select('tbody', true).first();
17242         
17243         time.dom.innerHTML = '';
17244         
17245         time.createChild({
17246             tag: 'tr',
17247             cn: [
17248                 {
17249                     tag: 'td',
17250                     cn: [
17251                         {
17252                             tag: 'a',
17253                             href: '#',
17254                             cls: 'btn',
17255                             cn: [
17256                                 {
17257                                     tag: 'span',
17258                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17259                                 }
17260                             ]
17261                         } 
17262                     ]
17263                 },
17264                 {
17265                     tag: 'td',
17266                     cls: 'separator'
17267                 },
17268                 {
17269                     tag: 'td',
17270                     cn: [
17271                         {
17272                             tag: 'a',
17273                             href: '#',
17274                             cls: 'btn',
17275                             cn: [
17276                                 {
17277                                     tag: 'span',
17278                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17279                                 }
17280                             ]
17281                         }
17282                     ]
17283                 },
17284                 {
17285                     tag: 'td',
17286                     cls: 'separator'
17287                 }
17288             ]
17289         });
17290         
17291         time.createChild({
17292             tag: 'tr',
17293             cn: [
17294                 {
17295                     tag: 'td',
17296                     cn: [
17297                         {
17298                             tag: 'span',
17299                             cls: 'timepicker-hour',
17300                             html: '00'
17301                         }  
17302                     ]
17303                 },
17304                 {
17305                     tag: 'td',
17306                     cls: 'separator',
17307                     html: ':'
17308                 },
17309                 {
17310                     tag: 'td',
17311                     cn: [
17312                         {
17313                             tag: 'span',
17314                             cls: 'timepicker-minute',
17315                             html: '00'
17316                         }  
17317                     ]
17318                 },
17319                 {
17320                     tag: 'td',
17321                     cls: 'separator'
17322                 },
17323                 {
17324                     tag: 'td',
17325                     cn: [
17326                         {
17327                             tag: 'button',
17328                             type: 'button',
17329                             cls: 'btn btn-primary period',
17330                             html: 'AM'
17331                             
17332                         }
17333                     ]
17334                 }
17335             ]
17336         });
17337         
17338         time.createChild({
17339             tag: 'tr',
17340             cn: [
17341                 {
17342                     tag: 'td',
17343                     cn: [
17344                         {
17345                             tag: 'a',
17346                             href: '#',
17347                             cls: 'btn',
17348                             cn: [
17349                                 {
17350                                     tag: 'span',
17351                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17352                                 }
17353                             ]
17354                         }
17355                     ]
17356                 },
17357                 {
17358                     tag: 'td',
17359                     cls: 'separator'
17360                 },
17361                 {
17362                     tag: 'td',
17363                     cn: [
17364                         {
17365                             tag: 'a',
17366                             href: '#',
17367                             cls: 'btn',
17368                             cn: [
17369                                 {
17370                                     tag: 'span',
17371                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17372                                 }
17373                             ]
17374                         }
17375                     ]
17376                 },
17377                 {
17378                     tag: 'td',
17379                     cls: 'separator'
17380                 }
17381             ]
17382         });
17383         
17384     },
17385     
17386     update: function()
17387     {
17388         
17389         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17390         
17391         this.fill();
17392     },
17393     
17394     fill: function() 
17395     {
17396         var hours = this.time.getHours();
17397         var minutes = this.time.getMinutes();
17398         var period = 'AM';
17399         
17400         if(hours > 11){
17401             period = 'PM';
17402         }
17403         
17404         if(hours == 0){
17405             hours = 12;
17406         }
17407         
17408         
17409         if(hours > 12){
17410             hours = hours - 12;
17411         }
17412         
17413         if(hours < 10){
17414             hours = '0' + hours;
17415         }
17416         
17417         if(minutes < 10){
17418             minutes = '0' + minutes;
17419         }
17420         
17421         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17422         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17423         this.pop.select('button', true).first().dom.innerHTML = period;
17424         
17425     },
17426     
17427     place: function()
17428     {   
17429         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17430         
17431         var cls = ['bottom'];
17432         
17433         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17434             cls.pop();
17435             cls.push('top');
17436         }
17437         
17438         cls.push('right');
17439         
17440         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17441             cls.pop();
17442             cls.push('left');
17443         }
17444         
17445         this.picker().addClass(cls.join('-'));
17446         
17447         var _this = this;
17448         
17449         Roo.each(cls, function(c){
17450             if(c == 'bottom'){
17451                 _this.picker().setTop(_this.inputEl().getHeight());
17452                 return;
17453             }
17454             if(c == 'top'){
17455                 _this.picker().setTop(0 - _this.picker().getHeight());
17456                 return;
17457             }
17458             
17459             if(c == 'left'){
17460                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17461                 return;
17462             }
17463             if(c == 'right'){
17464                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17465                 return;
17466             }
17467         });
17468         
17469     },
17470   
17471     onFocus : function()
17472     {
17473         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17474         this.show();
17475     },
17476     
17477     onBlur : function()
17478     {
17479         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17480         this.hide();
17481     },
17482     
17483     show : function()
17484     {
17485         this.picker().show();
17486         this.pop.show();
17487         this.update();
17488         this.place();
17489         
17490         this.fireEvent('show', this, this.date);
17491     },
17492     
17493     hide : function()
17494     {
17495         this.picker().hide();
17496         this.pop.hide();
17497         
17498         this.fireEvent('hide', this, this.date);
17499     },
17500     
17501     setTime : function()
17502     {
17503         this.hide();
17504         this.setValue(this.time.format(this.format));
17505         
17506         this.fireEvent('select', this, this.date);
17507         
17508         
17509     },
17510     
17511     onMousedown: function(e){
17512         e.stopPropagation();
17513         e.preventDefault();
17514     },
17515     
17516     onIncrementHours: function()
17517     {
17518         Roo.log('onIncrementHours');
17519         this.time = this.time.add(Date.HOUR, 1);
17520         this.update();
17521         
17522     },
17523     
17524     onDecrementHours: function()
17525     {
17526         Roo.log('onDecrementHours');
17527         this.time = this.time.add(Date.HOUR, -1);
17528         this.update();
17529     },
17530     
17531     onIncrementMinutes: function()
17532     {
17533         Roo.log('onIncrementMinutes');
17534         this.time = this.time.add(Date.MINUTE, 1);
17535         this.update();
17536     },
17537     
17538     onDecrementMinutes: function()
17539     {
17540         Roo.log('onDecrementMinutes');
17541         this.time = this.time.add(Date.MINUTE, -1);
17542         this.update();
17543     },
17544     
17545     onTogglePeriod: function()
17546     {
17547         Roo.log('onTogglePeriod');
17548         this.time = this.time.add(Date.HOUR, 12);
17549         this.update();
17550     }
17551     
17552    
17553 });
17554
17555 Roo.apply(Roo.bootstrap.TimeField,  {
17556     
17557     content : {
17558         tag: 'tbody',
17559         cn: [
17560             {
17561                 tag: 'tr',
17562                 cn: [
17563                 {
17564                     tag: 'td',
17565                     colspan: '7'
17566                 }
17567                 ]
17568             }
17569         ]
17570     },
17571     
17572     footer : {
17573         tag: 'tfoot',
17574         cn: [
17575             {
17576                 tag: 'tr',
17577                 cn: [
17578                 {
17579                     tag: 'th',
17580                     colspan: '7',
17581                     cls: '',
17582                     cn: [
17583                         {
17584                             tag: 'button',
17585                             cls: 'btn btn-info ok',
17586                             html: 'OK'
17587                         }
17588                     ]
17589                 }
17590
17591                 ]
17592             }
17593         ]
17594     }
17595 });
17596
17597 Roo.apply(Roo.bootstrap.TimeField,  {
17598   
17599     template : {
17600         tag: 'div',
17601         cls: 'datepicker dropdown-menu',
17602         cn: [
17603             {
17604                 tag: 'div',
17605                 cls: 'datepicker-time',
17606                 cn: [
17607                 {
17608                     tag: 'table',
17609                     cls: 'table-condensed',
17610                     cn:[
17611                     Roo.bootstrap.TimeField.content,
17612                     Roo.bootstrap.TimeField.footer
17613                     ]
17614                 }
17615                 ]
17616             }
17617         ]
17618     }
17619 });
17620
17621  
17622
17623  /*
17624  * - LGPL
17625  *
17626  * MonthField
17627  * 
17628  */
17629
17630 /**
17631  * @class Roo.bootstrap.MonthField
17632  * @extends Roo.bootstrap.Input
17633  * Bootstrap MonthField class
17634  * 
17635  * @cfg {String} language default en
17636  * 
17637  * @constructor
17638  * Create a new MonthField
17639  * @param {Object} config The config object
17640  */
17641
17642 Roo.bootstrap.MonthField = function(config){
17643     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17644     
17645     this.addEvents({
17646         /**
17647          * @event show
17648          * Fires when this field show.
17649          * @param {Roo.bootstrap.MonthField} this
17650          * @param {Mixed} date The date value
17651          */
17652         show : true,
17653         /**
17654          * @event show
17655          * Fires when this field hide.
17656          * @param {Roo.bootstrap.MonthField} this
17657          * @param {Mixed} date The date value
17658          */
17659         hide : true,
17660         /**
17661          * @event select
17662          * Fires when select a date.
17663          * @param {Roo.bootstrap.MonthField} this
17664          * @param {String} oldvalue The old value
17665          * @param {String} newvalue The new value
17666          */
17667         select : true
17668     });
17669 };
17670
17671 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17672     
17673     onRender: function(ct, position)
17674     {
17675         
17676         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17677         
17678         this.language = this.language || 'en';
17679         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17680         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17681         
17682         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17683         this.isInline = false;
17684         this.isInput = true;
17685         this.component = this.el.select('.add-on', true).first() || false;
17686         this.component = (this.component && this.component.length === 0) ? false : this.component;
17687         this.hasInput = this.component && this.inputEL().length;
17688         
17689         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17690         
17691         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17692         
17693         this.picker().on('mousedown', this.onMousedown, this);
17694         this.picker().on('click', this.onClick, this);
17695         
17696         this.picker().addClass('datepicker-dropdown');
17697         
17698         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17699             v.setStyle('width', '189px');
17700         });
17701         
17702         this.fillMonths();
17703         
17704         this.update();
17705         
17706         if(this.isInline) {
17707             this.show();
17708         }
17709         
17710     },
17711     
17712     setValue: function(v, suppressEvent)
17713     {   
17714         var o = this.getValue();
17715         
17716         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17717         
17718         this.update();
17719
17720         if(suppressEvent !== true){
17721             this.fireEvent('select', this, o, v);
17722         }
17723         
17724     },
17725     
17726     getValue: function()
17727     {
17728         return this.value;
17729     },
17730     
17731     onClick: function(e) 
17732     {
17733         e.stopPropagation();
17734         e.preventDefault();
17735         
17736         var target = e.getTarget();
17737         
17738         if(target.nodeName.toLowerCase() === 'i'){
17739             target = Roo.get(target).dom.parentNode;
17740         }
17741         
17742         var nodeName = target.nodeName;
17743         var className = target.className;
17744         var html = target.innerHTML;
17745         
17746         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17747             return;
17748         }
17749         
17750         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17751         
17752         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17753         
17754         this.hide();
17755                         
17756     },
17757     
17758     picker : function()
17759     {
17760         return this.pickerEl;
17761     },
17762     
17763     fillMonths: function()
17764     {    
17765         var i = 0;
17766         var months = this.picker().select('>.datepicker-months td', true).first();
17767         
17768         months.dom.innerHTML = '';
17769         
17770         while (i < 12) {
17771             var month = {
17772                 tag: 'span',
17773                 cls: 'month',
17774                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17775             }
17776             
17777             months.createChild(month);
17778         }
17779         
17780     },
17781     
17782     update: function()
17783     {
17784         var _this = this;
17785         
17786         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17787             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17788         }
17789         
17790         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17791             e.removeClass('active');
17792             
17793             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17794                 e.addClass('active');
17795             }
17796         })
17797     },
17798     
17799     place: function()
17800     {
17801         if(this.isInline) return;
17802         
17803         this.picker().removeClass(['bottom', 'top']);
17804         
17805         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17806             /*
17807              * place to the top of element!
17808              *
17809              */
17810             
17811             this.picker().addClass('top');
17812             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17813             
17814             return;
17815         }
17816         
17817         this.picker().addClass('bottom');
17818         
17819         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17820     },
17821     
17822     onFocus : function()
17823     {
17824         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17825         this.show();
17826     },
17827     
17828     onBlur : function()
17829     {
17830         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17831         
17832         var d = this.inputEl().getValue();
17833         
17834         this.setValue(d);
17835                 
17836         this.hide();
17837     },
17838     
17839     show : function()
17840     {
17841         this.picker().show();
17842         this.picker().select('>.datepicker-months', true).first().show();
17843         this.update();
17844         this.place();
17845         
17846         this.fireEvent('show', this, this.date);
17847     },
17848     
17849     hide : function()
17850     {
17851         if(this.isInline) return;
17852         this.picker().hide();
17853         this.fireEvent('hide', this, this.date);
17854         
17855     },
17856     
17857     onMousedown: function(e)
17858     {
17859         e.stopPropagation();
17860         e.preventDefault();
17861     },
17862     
17863     keyup: function(e)
17864     {
17865         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17866         this.update();
17867     },
17868
17869     fireKey: function(e)
17870     {
17871         if (!this.picker().isVisible()){
17872             if (e.keyCode == 27) // allow escape to hide and re-show picker
17873                 this.show();
17874             return;
17875         }
17876         
17877         var dir;
17878         
17879         switch(e.keyCode){
17880             case 27: // escape
17881                 this.hide();
17882                 e.preventDefault();
17883                 break;
17884             case 37: // left
17885             case 39: // right
17886                 dir = e.keyCode == 37 ? -1 : 1;
17887                 
17888                 this.vIndex = this.vIndex + dir;
17889                 
17890                 if(this.vIndex < 0){
17891                     this.vIndex = 0;
17892                 }
17893                 
17894                 if(this.vIndex > 11){
17895                     this.vIndex = 11;
17896                 }
17897                 
17898                 if(isNaN(this.vIndex)){
17899                     this.vIndex = 0;
17900                 }
17901                 
17902                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17903                 
17904                 break;
17905             case 38: // up
17906             case 40: // down
17907                 
17908                 dir = e.keyCode == 38 ? -1 : 1;
17909                 
17910                 this.vIndex = this.vIndex + dir * 4;
17911                 
17912                 if(this.vIndex < 0){
17913                     this.vIndex = 0;
17914                 }
17915                 
17916                 if(this.vIndex > 11){
17917                     this.vIndex = 11;
17918                 }
17919                 
17920                 if(isNaN(this.vIndex)){
17921                     this.vIndex = 0;
17922                 }
17923                 
17924                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17925                 break;
17926                 
17927             case 13: // enter
17928                 
17929                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17930                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17931                 }
17932                 
17933                 this.hide();
17934                 e.preventDefault();
17935                 break;
17936             case 9: // tab
17937                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17938                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17939                 }
17940                 this.hide();
17941                 break;
17942             case 16: // shift
17943             case 17: // ctrl
17944             case 18: // alt
17945                 break;
17946             default :
17947                 this.hide();
17948                 
17949         }
17950     },
17951     
17952     remove: function() 
17953     {
17954         this.picker().remove();
17955     }
17956    
17957 });
17958
17959 Roo.apply(Roo.bootstrap.MonthField,  {
17960     
17961     content : {
17962         tag: 'tbody',
17963         cn: [
17964         {
17965             tag: 'tr',
17966             cn: [
17967             {
17968                 tag: 'td',
17969                 colspan: '7'
17970             }
17971             ]
17972         }
17973         ]
17974     },
17975     
17976     dates:{
17977         en: {
17978             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17979             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17980         }
17981     }
17982 });
17983
17984 Roo.apply(Roo.bootstrap.MonthField,  {
17985   
17986     template : {
17987         tag: 'div',
17988         cls: 'datepicker dropdown-menu roo-dynamic',
17989         cn: [
17990             {
17991                 tag: 'div',
17992                 cls: 'datepicker-months',
17993                 cn: [
17994                 {
17995                     tag: 'table',
17996                     cls: 'table-condensed',
17997                     cn:[
17998                         Roo.bootstrap.DateField.content
17999                     ]
18000                 }
18001                 ]
18002             }
18003         ]
18004     }
18005 });
18006
18007  
18008
18009  
18010  /*
18011  * - LGPL
18012  *
18013  * CheckBox
18014  * 
18015  */
18016
18017 /**
18018  * @class Roo.bootstrap.CheckBox
18019  * @extends Roo.bootstrap.Input
18020  * Bootstrap CheckBox class
18021  * 
18022  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18023  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18024  * @cfg {String} boxLabel The text that appears beside the checkbox
18025  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18026  * @cfg {Boolean} checked initnal the element
18027  * @cfg {Boolean} inline inline the element (default false)
18028  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18029  * 
18030  * @constructor
18031  * Create a new CheckBox
18032  * @param {Object} config The config object
18033  */
18034
18035 Roo.bootstrap.CheckBox = function(config){
18036     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18037    
18038     this.addEvents({
18039         /**
18040         * @event check
18041         * Fires when the element is checked or unchecked.
18042         * @param {Roo.bootstrap.CheckBox} this This input
18043         * @param {Boolean} checked The new checked value
18044         */
18045        check : true
18046     });
18047     
18048 };
18049
18050 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18051   
18052     inputType: 'checkbox',
18053     inputValue: 1,
18054     valueOff: 0,
18055     boxLabel: false,
18056     checked: false,
18057     weight : false,
18058     inline: false,
18059     
18060     getAutoCreate : function()
18061     {
18062         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18063         
18064         var id = Roo.id();
18065         
18066         var cfg = {};
18067         
18068         cfg.cls = 'form-group ' + this.inputType; //input-group
18069         
18070         if(this.inline){
18071             cfg.cls += ' ' + this.inputType + '-inline';
18072         }
18073         
18074         var input =  {
18075             tag: 'input',
18076             id : id,
18077             type : this.inputType,
18078             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18079             cls : 'roo-' + this.inputType, //'form-box',
18080             placeholder : this.placeholder || ''
18081             
18082         };
18083         
18084         if (this.weight) { // Validity check?
18085             cfg.cls += " " + this.inputType + "-" + this.weight;
18086         }
18087         
18088         if (this.disabled) {
18089             input.disabled=true;
18090         }
18091         
18092         if(this.checked){
18093             input.checked = this.checked;
18094         }
18095         
18096         if (this.name) {
18097             input.name = this.name;
18098         }
18099         
18100         if (this.size) {
18101             input.cls += ' input-' + this.size;
18102         }
18103         
18104         var settings=this;
18105         
18106         ['xs','sm','md','lg'].map(function(size){
18107             if (settings[size]) {
18108                 cfg.cls += ' col-' + size + '-' + settings[size];
18109             }
18110         });
18111         
18112         var inputblock = input;
18113          
18114         if (this.before || this.after) {
18115             
18116             inputblock = {
18117                 cls : 'input-group',
18118                 cn :  [] 
18119             };
18120             
18121             if (this.before) {
18122                 inputblock.cn.push({
18123                     tag :'span',
18124                     cls : 'input-group-addon',
18125                     html : this.before
18126                 });
18127             }
18128             
18129             inputblock.cn.push(input);
18130             
18131             if (this.after) {
18132                 inputblock.cn.push({
18133                     tag :'span',
18134                     cls : 'input-group-addon',
18135                     html : this.after
18136                 });
18137             }
18138             
18139         }
18140         
18141         if (align ==='left' && this.fieldLabel.length) {
18142                 Roo.log("left and has label");
18143                 cfg.cn = [
18144                     
18145                     {
18146                         tag: 'label',
18147                         'for' :  id,
18148                         cls : 'control-label col-md-' + this.labelWidth,
18149                         html : this.fieldLabel
18150                         
18151                     },
18152                     {
18153                         cls : "col-md-" + (12 - this.labelWidth), 
18154                         cn: [
18155                             inputblock
18156                         ]
18157                     }
18158                     
18159                 ];
18160         } else if ( this.fieldLabel.length) {
18161                 Roo.log(" label");
18162                 cfg.cn = [
18163                    
18164                     {
18165                         tag: this.boxLabel ? 'span' : 'label',
18166                         'for': id,
18167                         cls: 'control-label box-input-label',
18168                         //cls : 'input-group-addon',
18169                         html : this.fieldLabel
18170                         
18171                     },
18172                     
18173                     inputblock
18174                     
18175                 ];
18176
18177         } else {
18178             
18179                 Roo.log(" no label && no align");
18180                 cfg.cn = [  inputblock ] ;
18181                 
18182                 
18183         }
18184         if(this.boxLabel){
18185              var boxLabelCfg = {
18186                 tag: 'label',
18187                 //'for': id, // box label is handled by onclick - so no for...
18188                 cls: 'box-label',
18189                 html: this.boxLabel
18190             }
18191             
18192             if(this.tooltip){
18193                 boxLabelCfg.tooltip = this.tooltip;
18194             }
18195              
18196             cfg.cn.push(boxLabelCfg);
18197         }
18198         
18199         
18200        
18201         return cfg;
18202         
18203     },
18204     
18205     /**
18206      * return the real input element.
18207      */
18208     inputEl: function ()
18209     {
18210         return this.el.select('input.roo-' + this.inputType,true).first();
18211     },
18212     
18213     labelEl: function()
18214     {
18215         return this.el.select('label.control-label',true).first();
18216     },
18217     /* depricated... */
18218     
18219     label: function()
18220     {
18221         return this.labelEl();
18222     },
18223     
18224     initEvents : function()
18225     {
18226 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18227         
18228         this.inputEl().on('click', this.onClick,  this);
18229         
18230         if (this.boxLabel) { 
18231             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18232         }
18233         
18234         this.startValue = this.getValue();
18235         
18236         if(this.groupId){
18237             Roo.bootstrap.CheckBox.register(this);
18238         }
18239     },
18240     
18241     onClick : function()
18242     {   
18243         this.setChecked(!this.checked);
18244     },
18245     
18246     setChecked : function(state,suppressEvent)
18247     {
18248         this.startValue = this.getValue();
18249         
18250         if(this.inputType == 'radio'){
18251             
18252             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18253                 e.dom.checked = false;
18254             });
18255             
18256             this.inputEl().dom.checked = true;
18257             
18258             this.inputEl().dom.value = this.inputValue;
18259             
18260             if(suppressEvent !== true){
18261                 this.fireEvent('check', this, true);
18262             }
18263             
18264             this.validate();
18265             
18266             return;
18267         }
18268         
18269         this.checked = state;
18270         
18271         this.inputEl().dom.checked = state;
18272         
18273         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18274         
18275         if(suppressEvent !== true){
18276             this.fireEvent('check', this, state);
18277         }
18278         
18279         this.validate();
18280     },
18281     
18282     getValue : function()
18283     {
18284         if(this.inputType == 'radio'){
18285             return this.getGroupValue();
18286         }
18287         
18288         return this.inputEl().getValue();
18289         
18290     },
18291     
18292     getGroupValue : function()
18293     {
18294         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18295             return '';
18296         }
18297         
18298         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18299     },
18300     
18301     setValue : function(v,suppressEvent)
18302     {
18303         if(this.inputType == 'radio'){
18304             this.setGroupValue(v, suppressEvent);
18305             return;
18306         }
18307         
18308         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18309         
18310         this.validate();
18311     },
18312     
18313     setGroupValue : function(v, suppressEvent)
18314     {
18315         this.startValue = this.getValue();
18316         
18317         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18318             e.dom.checked = false;
18319             
18320             if(e.dom.value == v){
18321                 e.dom.checked = true;
18322             }
18323         });
18324         
18325         if(suppressEvent !== true){
18326             this.fireEvent('check', this, true);
18327         }
18328
18329         this.validate();
18330         
18331         return;
18332     },
18333     
18334     validate : function()
18335     {
18336         if(
18337                 this.disabled || 
18338                 (this.inputType == 'radio' && this.validateRadio()) ||
18339                 (this.inputType == 'checkbox' && this.validateCheckbox())
18340         ){
18341             this.markValid();
18342             return true;
18343         }
18344         
18345         this.markInvalid();
18346         return false;
18347     },
18348     
18349     validateRadio : function()
18350     {
18351         var valid = false;
18352         
18353         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18354             if(!e.dom.checked){
18355                 return;
18356             }
18357             
18358             valid = true;
18359             
18360             return false;
18361         });
18362         
18363         return valid;
18364     },
18365     
18366     validateCheckbox : function()
18367     {
18368         if(!this.groupId){
18369             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18370         }
18371         
18372         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18373         
18374         if(!group){
18375             return false;
18376         }
18377         
18378         var r = false;
18379         
18380         for(var i in group){
18381             if(r){
18382                 break;
18383             }
18384             
18385             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18386         }
18387         
18388         return r;
18389     },
18390     
18391     /**
18392      * Mark this field as valid
18393      */
18394     markValid : function()
18395     {
18396         if(this.allowBlank){
18397             return;
18398         }
18399         
18400         var _this = this;
18401         
18402         this.fireEvent('valid', this);
18403         
18404         if(this.inputType == 'radio'){
18405             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18406                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18407                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18408             });
18409             
18410             return;
18411         }
18412         
18413         if(!this.groupId){
18414             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18415             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18416             return;
18417         }
18418         
18419         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18420             
18421         if(!group){
18422             return;
18423         }
18424         
18425         for(var i in group){
18426             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18427             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18428         }
18429     },
18430     
18431      /**
18432      * Mark this field as invalid
18433      * @param {String} msg The validation message
18434      */
18435     markInvalid : function(msg)
18436     {
18437         if(this.allowBlank){
18438             return;
18439         }
18440         
18441         var _this = this;
18442         
18443         this.fireEvent('invalid', this, msg);
18444         
18445         if(this.inputType == 'radio'){
18446             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18447                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18448                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18449             });
18450             
18451             return;
18452         }
18453         
18454         if(!this.groupId){
18455             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18456             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18457             return;
18458         }
18459         
18460         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18461             
18462         if(!group){
18463             return;
18464         }
18465         
18466         for(var i in group){
18467             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18468             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18469         }
18470         
18471     }
18472     
18473 });
18474
18475 Roo.apply(Roo.bootstrap.CheckBox, {
18476     
18477     groups: {},
18478     
18479      /**
18480     * register a CheckBox Group
18481     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18482     */
18483     register : function(checkbox)
18484     {
18485         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18486             this.groups[checkbox.groupId] = {};
18487         }
18488         
18489         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18490             return;
18491         }
18492         
18493         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18494         
18495     },
18496     /**
18497     * fetch a CheckBox Group based on the group ID
18498     * @param {string} the group ID
18499     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18500     */
18501     get: function(groupId) {
18502         if (typeof(this.groups[groupId]) == 'undefined') {
18503             return false;
18504         }
18505         
18506         return this.groups[groupId] ;
18507     }
18508     
18509     
18510 });
18511 /*
18512  * - LGPL
18513  *
18514  * Radio
18515  *
18516  *
18517  * not inline
18518  *<div class="radio">
18519   <label>
18520     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18521     Option one is this and that&mdash;be sure to include why it's great
18522   </label>
18523 </div>
18524  *
18525  *
18526  *inline
18527  *<span>
18528  *<label class="radio-inline">fieldLabel</label>
18529  *<label class="radio-inline">
18530   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18531 </label>
18532 <span>
18533  * 
18534  * 
18535  */
18536
18537 /**
18538  * @class Roo.bootstrap.Radio
18539  * @extends Roo.bootstrap.CheckBox
18540  * Bootstrap Radio class
18541
18542  * @constructor
18543  * Create a new Radio
18544  * @param {Object} config The config object
18545  */
18546
18547 Roo.bootstrap.Radio = function(config){
18548     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18549    
18550 };
18551
18552 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18553     
18554     inputType: 'radio',
18555     inputValue: '',
18556     valueOff: '',
18557     
18558     getAutoCreate : function()
18559     {
18560         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18561         align = align || 'left'; // default...
18562         
18563         
18564         
18565         var id = Roo.id();
18566         
18567         var cfg = {
18568                 tag : this.inline ? 'span' : 'div',
18569                 cls : '',
18570                 cn : []
18571         };
18572         
18573         var inline = this.inline ? ' radio-inline' : '';
18574         
18575         var lbl = {
18576                 tag: 'label' ,
18577                 // does not need for, as we wrap the input with it..
18578                 'for' : id,
18579                 cls : 'control-label box-label' + inline,
18580                 cn : []
18581         };
18582         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18583         
18584         var fieldLabel = {
18585             tag: 'label' ,
18586             //cls : 'control-label' + inline,
18587             html : this.fieldLabel,
18588             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18589         };
18590         
18591  
18592         
18593         
18594         var input =  {
18595             tag: 'input',
18596             id : id,
18597             type : this.inputType,
18598             //value : (!this.checked) ? this.valueOff : this.inputValue,
18599             value : this.inputValue,
18600             cls : 'roo-radio',
18601             placeholder : this.placeholder || '' // ?? needed????
18602             
18603         };
18604         if (this.weight) { // Validity check?
18605             input.cls += " radio-" + this.weight;
18606         }
18607         if (this.disabled) {
18608             input.disabled=true;
18609         }
18610         
18611         if(this.checked){
18612             input.checked = this.checked;
18613         }
18614         
18615         if (this.name) {
18616             input.name = this.name;
18617         }
18618         
18619         if (this.size) {
18620             input.cls += ' input-' + this.size;
18621         }
18622         
18623         //?? can span's inline have a width??
18624         
18625         var settings=this;
18626         ['xs','sm','md','lg'].map(function(size){
18627             if (settings[size]) {
18628                 cfg.cls += ' col-' + size + '-' + settings[size];
18629             }
18630         });
18631         
18632         var inputblock = input;
18633         
18634         if (this.before || this.after) {
18635             
18636             inputblock = {
18637                 cls : 'input-group',
18638                 tag : 'span',
18639                 cn :  [] 
18640             };
18641             if (this.before) {
18642                 inputblock.cn.push({
18643                     tag :'span',
18644                     cls : 'input-group-addon',
18645                     html : this.before
18646                 });
18647             }
18648             inputblock.cn.push(input);
18649             if (this.after) {
18650                 inputblock.cn.push({
18651                     tag :'span',
18652                     cls : 'input-group-addon',
18653                     html : this.after
18654                 });
18655             }
18656             
18657         };
18658         
18659         
18660         if (this.fieldLabel && this.fieldLabel.length) {
18661             cfg.cn.push(fieldLabel);
18662         }
18663        
18664         // normal bootstrap puts the input inside the label.
18665         // however with our styled version - it has to go after the input.
18666        
18667         //lbl.cn.push(inputblock);
18668         
18669         var lblwrap =  {
18670             tag: 'span',
18671             cls: 'radio' + inline,
18672             cn: [
18673                 inputblock,
18674                 lbl
18675             ]
18676         };
18677         
18678         cfg.cn.push( lblwrap);
18679         
18680         if(this.boxLabel){
18681             lbl.cn.push({
18682                 tag: 'span',
18683                 html: this.boxLabel
18684             })
18685         }
18686          
18687         
18688         return cfg;
18689         
18690     },
18691     
18692     initEvents : function()
18693     {
18694 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18695         
18696         this.inputEl().on('click', this.onClick,  this);
18697         if (this.boxLabel) {
18698             Roo.log('find label')
18699             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18700         }
18701         
18702     },
18703     
18704     inputEl: function ()
18705     {
18706         return this.el.select('input.roo-radio',true).first();
18707     },
18708     onClick : function()
18709     {   
18710         Roo.log("click");
18711         this.setChecked(true);
18712     },
18713     
18714     setChecked : function(state,suppressEvent)
18715     {
18716         if(state){
18717             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18718                 v.dom.checked = false;
18719             });
18720         }
18721         Roo.log(this.inputEl().dom);
18722         this.checked = state;
18723         this.inputEl().dom.checked = state;
18724         
18725         if(suppressEvent !== true){
18726             this.fireEvent('check', this, state);
18727         }
18728         
18729         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18730         
18731     },
18732     
18733     getGroupValue : function()
18734     {
18735         var value = '';
18736         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18737             if(v.dom.checked == true){
18738                 value = v.dom.value;
18739             }
18740         });
18741         
18742         return value;
18743     },
18744     
18745     /**
18746      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18747      * @return {Mixed} value The field value
18748      */
18749     getValue : function(){
18750         return this.getGroupValue();
18751     }
18752     
18753 });
18754
18755  
18756 //<script type="text/javascript">
18757
18758 /*
18759  * Based  Ext JS Library 1.1.1
18760  * Copyright(c) 2006-2007, Ext JS, LLC.
18761  * LGPL
18762  *
18763  */
18764  
18765 /**
18766  * @class Roo.HtmlEditorCore
18767  * @extends Roo.Component
18768  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18769  *
18770  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18771  */
18772
18773 Roo.HtmlEditorCore = function(config){
18774     
18775     
18776     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18777     
18778     
18779     this.addEvents({
18780         /**
18781          * @event initialize
18782          * Fires when the editor is fully initialized (including the iframe)
18783          * @param {Roo.HtmlEditorCore} this
18784          */
18785         initialize: true,
18786         /**
18787          * @event activate
18788          * Fires when the editor is first receives the focus. Any insertion must wait
18789          * until after this event.
18790          * @param {Roo.HtmlEditorCore} this
18791          */
18792         activate: true,
18793          /**
18794          * @event beforesync
18795          * Fires before the textarea is updated with content from the editor iframe. Return false
18796          * to cancel the sync.
18797          * @param {Roo.HtmlEditorCore} this
18798          * @param {String} html
18799          */
18800         beforesync: true,
18801          /**
18802          * @event beforepush
18803          * Fires before the iframe editor is updated with content from the textarea. Return false
18804          * to cancel the push.
18805          * @param {Roo.HtmlEditorCore} this
18806          * @param {String} html
18807          */
18808         beforepush: true,
18809          /**
18810          * @event sync
18811          * Fires when the textarea is updated with content from the editor iframe.
18812          * @param {Roo.HtmlEditorCore} this
18813          * @param {String} html
18814          */
18815         sync: true,
18816          /**
18817          * @event push
18818          * Fires when the iframe editor is updated with content from the textarea.
18819          * @param {Roo.HtmlEditorCore} this
18820          * @param {String} html
18821          */
18822         push: true,
18823         
18824         /**
18825          * @event editorevent
18826          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18827          * @param {Roo.HtmlEditorCore} this
18828          */
18829         editorevent: true
18830         
18831     });
18832     
18833     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18834     
18835     // defaults : white / black...
18836     this.applyBlacklists();
18837     
18838     
18839     
18840 };
18841
18842
18843 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18844
18845
18846      /**
18847      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18848      */
18849     
18850     owner : false,
18851     
18852      /**
18853      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18854      *                        Roo.resizable.
18855      */
18856     resizable : false,
18857      /**
18858      * @cfg {Number} height (in pixels)
18859      */   
18860     height: 300,
18861    /**
18862      * @cfg {Number} width (in pixels)
18863      */   
18864     width: 500,
18865     
18866     /**
18867      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18868      * 
18869      */
18870     stylesheets: false,
18871     
18872     // id of frame..
18873     frameId: false,
18874     
18875     // private properties
18876     validationEvent : false,
18877     deferHeight: true,
18878     initialized : false,
18879     activated : false,
18880     sourceEditMode : false,
18881     onFocus : Roo.emptyFn,
18882     iframePad:3,
18883     hideMode:'offsets',
18884     
18885     clearUp: true,
18886     
18887     // blacklist + whitelisted elements..
18888     black: false,
18889     white: false,
18890      
18891     
18892
18893     /**
18894      * Protected method that will not generally be called directly. It
18895      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18896      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18897      */
18898     getDocMarkup : function(){
18899         // body styles..
18900         var st = '';
18901         
18902         // inherit styels from page...?? 
18903         if (this.stylesheets === false) {
18904             
18905             Roo.get(document.head).select('style').each(function(node) {
18906                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18907             });
18908             
18909             Roo.get(document.head).select('link').each(function(node) { 
18910                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18911             });
18912             
18913         } else if (!this.stylesheets.length) {
18914                 // simple..
18915                 st = '<style type="text/css">' +
18916                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18917                    '</style>';
18918         } else { 
18919             
18920         }
18921         
18922         st +=  '<style type="text/css">' +
18923             'IMG { cursor: pointer } ' +
18924         '</style>';
18925
18926         
18927         return '<html><head>' + st  +
18928             //<style type="text/css">' +
18929             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18930             //'</style>' +
18931             ' </head><body class="roo-htmleditor-body"></body></html>';
18932     },
18933
18934     // private
18935     onRender : function(ct, position)
18936     {
18937         var _t = this;
18938         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18939         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18940         
18941         
18942         this.el.dom.style.border = '0 none';
18943         this.el.dom.setAttribute('tabIndex', -1);
18944         this.el.addClass('x-hidden hide');
18945         
18946         
18947         
18948         if(Roo.isIE){ // fix IE 1px bogus margin
18949             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18950         }
18951        
18952         
18953         this.frameId = Roo.id();
18954         
18955          
18956         
18957         var iframe = this.owner.wrap.createChild({
18958             tag: 'iframe',
18959             cls: 'form-control', // bootstrap..
18960             id: this.frameId,
18961             name: this.frameId,
18962             frameBorder : 'no',
18963             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18964         }, this.el
18965         );
18966         
18967         
18968         this.iframe = iframe.dom;
18969
18970          this.assignDocWin();
18971         
18972         this.doc.designMode = 'on';
18973        
18974         this.doc.open();
18975         this.doc.write(this.getDocMarkup());
18976         this.doc.close();
18977
18978         
18979         var task = { // must defer to wait for browser to be ready
18980             run : function(){
18981                 //console.log("run task?" + this.doc.readyState);
18982                 this.assignDocWin();
18983                 if(this.doc.body || this.doc.readyState == 'complete'){
18984                     try {
18985                         this.doc.designMode="on";
18986                     } catch (e) {
18987                         return;
18988                     }
18989                     Roo.TaskMgr.stop(task);
18990                     this.initEditor.defer(10, this);
18991                 }
18992             },
18993             interval : 10,
18994             duration: 10000,
18995             scope: this
18996         };
18997         Roo.TaskMgr.start(task);
18998
18999     },
19000
19001     // private
19002     onResize : function(w, h)
19003     {
19004          Roo.log('resize: ' +w + ',' + h );
19005         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19006         if(!this.iframe){
19007             return;
19008         }
19009         if(typeof w == 'number'){
19010             
19011             this.iframe.style.width = w + 'px';
19012         }
19013         if(typeof h == 'number'){
19014             
19015             this.iframe.style.height = h + 'px';
19016             if(this.doc){
19017                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19018             }
19019         }
19020         
19021     },
19022
19023     /**
19024      * Toggles the editor between standard and source edit mode.
19025      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19026      */
19027     toggleSourceEdit : function(sourceEditMode){
19028         
19029         this.sourceEditMode = sourceEditMode === true;
19030         
19031         if(this.sourceEditMode){
19032  
19033             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19034             
19035         }else{
19036             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19037             //this.iframe.className = '';
19038             this.deferFocus();
19039         }
19040         //this.setSize(this.owner.wrap.getSize());
19041         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19042     },
19043
19044     
19045   
19046
19047     /**
19048      * Protected method that will not generally be called directly. If you need/want
19049      * custom HTML cleanup, this is the method you should override.
19050      * @param {String} html The HTML to be cleaned
19051      * return {String} The cleaned HTML
19052      */
19053     cleanHtml : function(html){
19054         html = String(html);
19055         if(html.length > 5){
19056             if(Roo.isSafari){ // strip safari nonsense
19057                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19058             }
19059         }
19060         if(html == '&nbsp;'){
19061             html = '';
19062         }
19063         return html;
19064     },
19065
19066     /**
19067      * HTML Editor -> Textarea
19068      * Protected method that will not generally be called directly. Syncs the contents
19069      * of the editor iframe with the textarea.
19070      */
19071     syncValue : function(){
19072         if(this.initialized){
19073             var bd = (this.doc.body || this.doc.documentElement);
19074             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19075             var html = bd.innerHTML;
19076             if(Roo.isSafari){
19077                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19078                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19079                 if(m && m[1]){
19080                     html = '<div style="'+m[0]+'">' + html + '</div>';
19081                 }
19082             }
19083             html = this.cleanHtml(html);
19084             // fix up the special chars.. normaly like back quotes in word...
19085             // however we do not want to do this with chinese..
19086             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19087                 var cc = b.charCodeAt();
19088                 if (
19089                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19090                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19091                     (cc >= 0xf900 && cc < 0xfb00 )
19092                 ) {
19093                         return b;
19094                 }
19095                 return "&#"+cc+";" 
19096             });
19097             if(this.owner.fireEvent('beforesync', this, html) !== false){
19098                 this.el.dom.value = html;
19099                 this.owner.fireEvent('sync', this, html);
19100             }
19101         }
19102     },
19103
19104     /**
19105      * Protected method that will not generally be called directly. Pushes the value of the textarea
19106      * into the iframe editor.
19107      */
19108     pushValue : function(){
19109         if(this.initialized){
19110             var v = this.el.dom.value.trim();
19111             
19112 //            if(v.length < 1){
19113 //                v = '&#160;';
19114 //            }
19115             
19116             if(this.owner.fireEvent('beforepush', this, v) !== false){
19117                 var d = (this.doc.body || this.doc.documentElement);
19118                 d.innerHTML = v;
19119                 this.cleanUpPaste();
19120                 this.el.dom.value = d.innerHTML;
19121                 this.owner.fireEvent('push', this, v);
19122             }
19123         }
19124     },
19125
19126     // private
19127     deferFocus : function(){
19128         this.focus.defer(10, this);
19129     },
19130
19131     // doc'ed in Field
19132     focus : function(){
19133         if(this.win && !this.sourceEditMode){
19134             this.win.focus();
19135         }else{
19136             this.el.focus();
19137         }
19138     },
19139     
19140     assignDocWin: function()
19141     {
19142         var iframe = this.iframe;
19143         
19144          if(Roo.isIE){
19145             this.doc = iframe.contentWindow.document;
19146             this.win = iframe.contentWindow;
19147         } else {
19148 //            if (!Roo.get(this.frameId)) {
19149 //                return;
19150 //            }
19151 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19152 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19153             
19154             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19155                 return;
19156             }
19157             
19158             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19159             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19160         }
19161     },
19162     
19163     // private
19164     initEditor : function(){
19165         //console.log("INIT EDITOR");
19166         this.assignDocWin();
19167         
19168         
19169         
19170         this.doc.designMode="on";
19171         this.doc.open();
19172         this.doc.write(this.getDocMarkup());
19173         this.doc.close();
19174         
19175         var dbody = (this.doc.body || this.doc.documentElement);
19176         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19177         // this copies styles from the containing element into thsi one..
19178         // not sure why we need all of this..
19179         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19180         
19181         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19182         //ss['background-attachment'] = 'fixed'; // w3c
19183         dbody.bgProperties = 'fixed'; // ie
19184         //Roo.DomHelper.applyStyles(dbody, ss);
19185         Roo.EventManager.on(this.doc, {
19186             //'mousedown': this.onEditorEvent,
19187             'mouseup': this.onEditorEvent,
19188             'dblclick': this.onEditorEvent,
19189             'click': this.onEditorEvent,
19190             'keyup': this.onEditorEvent,
19191             buffer:100,
19192             scope: this
19193         });
19194         if(Roo.isGecko){
19195             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19196         }
19197         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19198             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19199         }
19200         this.initialized = true;
19201
19202         this.owner.fireEvent('initialize', this);
19203         this.pushValue();
19204     },
19205
19206     // private
19207     onDestroy : function(){
19208         
19209         
19210         
19211         if(this.rendered){
19212             
19213             //for (var i =0; i < this.toolbars.length;i++) {
19214             //    // fixme - ask toolbars for heights?
19215             //    this.toolbars[i].onDestroy();
19216            // }
19217             
19218             //this.wrap.dom.innerHTML = '';
19219             //this.wrap.remove();
19220         }
19221     },
19222
19223     // private
19224     onFirstFocus : function(){
19225         
19226         this.assignDocWin();
19227         
19228         
19229         this.activated = true;
19230          
19231     
19232         if(Roo.isGecko){ // prevent silly gecko errors
19233             this.win.focus();
19234             var s = this.win.getSelection();
19235             if(!s.focusNode || s.focusNode.nodeType != 3){
19236                 var r = s.getRangeAt(0);
19237                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19238                 r.collapse(true);
19239                 this.deferFocus();
19240             }
19241             try{
19242                 this.execCmd('useCSS', true);
19243                 this.execCmd('styleWithCSS', false);
19244             }catch(e){}
19245         }
19246         this.owner.fireEvent('activate', this);
19247     },
19248
19249     // private
19250     adjustFont: function(btn){
19251         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19252         //if(Roo.isSafari){ // safari
19253         //    adjust *= 2;
19254        // }
19255         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19256         if(Roo.isSafari){ // safari
19257             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19258             v =  (v < 10) ? 10 : v;
19259             v =  (v > 48) ? 48 : v;
19260             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19261             
19262         }
19263         
19264         
19265         v = Math.max(1, v+adjust);
19266         
19267         this.execCmd('FontSize', v  );
19268     },
19269
19270     onEditorEvent : function(e)
19271     {
19272         this.owner.fireEvent('editorevent', this, e);
19273       //  this.updateToolbar();
19274         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19275     },
19276
19277     insertTag : function(tg)
19278     {
19279         // could be a bit smarter... -> wrap the current selected tRoo..
19280         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19281             
19282             range = this.createRange(this.getSelection());
19283             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19284             wrappingNode.appendChild(range.extractContents());
19285             range.insertNode(wrappingNode);
19286
19287             return;
19288             
19289             
19290             
19291         }
19292         this.execCmd("formatblock",   tg);
19293         
19294     },
19295     
19296     insertText : function(txt)
19297     {
19298         
19299         
19300         var range = this.createRange();
19301         range.deleteContents();
19302                //alert(Sender.getAttribute('label'));
19303                
19304         range.insertNode(this.doc.createTextNode(txt));
19305     } ,
19306     
19307      
19308
19309     /**
19310      * Executes a Midas editor command on the editor document and performs necessary focus and
19311      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19312      * @param {String} cmd The Midas command
19313      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19314      */
19315     relayCmd : function(cmd, value){
19316         this.win.focus();
19317         this.execCmd(cmd, value);
19318         this.owner.fireEvent('editorevent', this);
19319         //this.updateToolbar();
19320         this.owner.deferFocus();
19321     },
19322
19323     /**
19324      * Executes a Midas editor command directly on the editor document.
19325      * For visual commands, you should use {@link #relayCmd} instead.
19326      * <b>This should only be called after the editor is initialized.</b>
19327      * @param {String} cmd The Midas command
19328      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19329      */
19330     execCmd : function(cmd, value){
19331         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19332         this.syncValue();
19333     },
19334  
19335  
19336    
19337     /**
19338      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19339      * to insert tRoo.
19340      * @param {String} text | dom node.. 
19341      */
19342     insertAtCursor : function(text)
19343     {
19344         
19345         
19346         
19347         if(!this.activated){
19348             return;
19349         }
19350         /*
19351         if(Roo.isIE){
19352             this.win.focus();
19353             var r = this.doc.selection.createRange();
19354             if(r){
19355                 r.collapse(true);
19356                 r.pasteHTML(text);
19357                 this.syncValue();
19358                 this.deferFocus();
19359             
19360             }
19361             return;
19362         }
19363         */
19364         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19365             this.win.focus();
19366             
19367             
19368             // from jquery ui (MIT licenced)
19369             var range, node;
19370             var win = this.win;
19371             
19372             if (win.getSelection && win.getSelection().getRangeAt) {
19373                 range = win.getSelection().getRangeAt(0);
19374                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19375                 range.insertNode(node);
19376             } else if (win.document.selection && win.document.selection.createRange) {
19377                 // no firefox support
19378                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19379                 win.document.selection.createRange().pasteHTML(txt);
19380             } else {
19381                 // no firefox support
19382                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19383                 this.execCmd('InsertHTML', txt);
19384             } 
19385             
19386             this.syncValue();
19387             
19388             this.deferFocus();
19389         }
19390     },
19391  // private
19392     mozKeyPress : function(e){
19393         if(e.ctrlKey){
19394             var c = e.getCharCode(), cmd;
19395           
19396             if(c > 0){
19397                 c = String.fromCharCode(c).toLowerCase();
19398                 switch(c){
19399                     case 'b':
19400                         cmd = 'bold';
19401                         break;
19402                     case 'i':
19403                         cmd = 'italic';
19404                         break;
19405                     
19406                     case 'u':
19407                         cmd = 'underline';
19408                         break;
19409                     
19410                     case 'v':
19411                         this.cleanUpPaste.defer(100, this);
19412                         return;
19413                         
19414                 }
19415                 if(cmd){
19416                     this.win.focus();
19417                     this.execCmd(cmd);
19418                     this.deferFocus();
19419                     e.preventDefault();
19420                 }
19421                 
19422             }
19423         }
19424     },
19425
19426     // private
19427     fixKeys : function(){ // load time branching for fastest keydown performance
19428         if(Roo.isIE){
19429             return function(e){
19430                 var k = e.getKey(), r;
19431                 if(k == e.TAB){
19432                     e.stopEvent();
19433                     r = this.doc.selection.createRange();
19434                     if(r){
19435                         r.collapse(true);
19436                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19437                         this.deferFocus();
19438                     }
19439                     return;
19440                 }
19441                 
19442                 if(k == e.ENTER){
19443                     r = this.doc.selection.createRange();
19444                     if(r){
19445                         var target = r.parentElement();
19446                         if(!target || target.tagName.toLowerCase() != 'li'){
19447                             e.stopEvent();
19448                             r.pasteHTML('<br />');
19449                             r.collapse(false);
19450                             r.select();
19451                         }
19452                     }
19453                 }
19454                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19455                     this.cleanUpPaste.defer(100, this);
19456                     return;
19457                 }
19458                 
19459                 
19460             };
19461         }else if(Roo.isOpera){
19462             return function(e){
19463                 var k = e.getKey();
19464                 if(k == e.TAB){
19465                     e.stopEvent();
19466                     this.win.focus();
19467                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19468                     this.deferFocus();
19469                 }
19470                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19471                     this.cleanUpPaste.defer(100, this);
19472                     return;
19473                 }
19474                 
19475             };
19476         }else if(Roo.isSafari){
19477             return function(e){
19478                 var k = e.getKey();
19479                 
19480                 if(k == e.TAB){
19481                     e.stopEvent();
19482                     this.execCmd('InsertText','\t');
19483                     this.deferFocus();
19484                     return;
19485                 }
19486                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19487                     this.cleanUpPaste.defer(100, this);
19488                     return;
19489                 }
19490                 
19491              };
19492         }
19493     }(),
19494     
19495     getAllAncestors: function()
19496     {
19497         var p = this.getSelectedNode();
19498         var a = [];
19499         if (!p) {
19500             a.push(p); // push blank onto stack..
19501             p = this.getParentElement();
19502         }
19503         
19504         
19505         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19506             a.push(p);
19507             p = p.parentNode;
19508         }
19509         a.push(this.doc.body);
19510         return a;
19511     },
19512     lastSel : false,
19513     lastSelNode : false,
19514     
19515     
19516     getSelection : function() 
19517     {
19518         this.assignDocWin();
19519         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19520     },
19521     
19522     getSelectedNode: function() 
19523     {
19524         // this may only work on Gecko!!!
19525         
19526         // should we cache this!!!!
19527         
19528         
19529         
19530          
19531         var range = this.createRange(this.getSelection()).cloneRange();
19532         
19533         if (Roo.isIE) {
19534             var parent = range.parentElement();
19535             while (true) {
19536                 var testRange = range.duplicate();
19537                 testRange.moveToElementText(parent);
19538                 if (testRange.inRange(range)) {
19539                     break;
19540                 }
19541                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19542                     break;
19543                 }
19544                 parent = parent.parentElement;
19545             }
19546             return parent;
19547         }
19548         
19549         // is ancestor a text element.
19550         var ac =  range.commonAncestorContainer;
19551         if (ac.nodeType == 3) {
19552             ac = ac.parentNode;
19553         }
19554         
19555         var ar = ac.childNodes;
19556          
19557         var nodes = [];
19558         var other_nodes = [];
19559         var has_other_nodes = false;
19560         for (var i=0;i<ar.length;i++) {
19561             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19562                 continue;
19563             }
19564             // fullly contained node.
19565             
19566             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19567                 nodes.push(ar[i]);
19568                 continue;
19569             }
19570             
19571             // probably selected..
19572             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19573                 other_nodes.push(ar[i]);
19574                 continue;
19575             }
19576             // outer..
19577             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19578                 continue;
19579             }
19580             
19581             
19582             has_other_nodes = true;
19583         }
19584         if (!nodes.length && other_nodes.length) {
19585             nodes= other_nodes;
19586         }
19587         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19588             return false;
19589         }
19590         
19591         return nodes[0];
19592     },
19593     createRange: function(sel)
19594     {
19595         // this has strange effects when using with 
19596         // top toolbar - not sure if it's a great idea.
19597         //this.editor.contentWindow.focus();
19598         if (typeof sel != "undefined") {
19599             try {
19600                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19601             } catch(e) {
19602                 return this.doc.createRange();
19603             }
19604         } else {
19605             return this.doc.createRange();
19606         }
19607     },
19608     getParentElement: function()
19609     {
19610         
19611         this.assignDocWin();
19612         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19613         
19614         var range = this.createRange(sel);
19615          
19616         try {
19617             var p = range.commonAncestorContainer;
19618             while (p.nodeType == 3) { // text node
19619                 p = p.parentNode;
19620             }
19621             return p;
19622         } catch (e) {
19623             return null;
19624         }
19625     
19626     },
19627     /***
19628      *
19629      * Range intersection.. the hard stuff...
19630      *  '-1' = before
19631      *  '0' = hits..
19632      *  '1' = after.
19633      *         [ -- selected range --- ]
19634      *   [fail]                        [fail]
19635      *
19636      *    basically..
19637      *      if end is before start or  hits it. fail.
19638      *      if start is after end or hits it fail.
19639      *
19640      *   if either hits (but other is outside. - then it's not 
19641      *   
19642      *    
19643      **/
19644     
19645     
19646     // @see http://www.thismuchiknow.co.uk/?p=64.
19647     rangeIntersectsNode : function(range, node)
19648     {
19649         var nodeRange = node.ownerDocument.createRange();
19650         try {
19651             nodeRange.selectNode(node);
19652         } catch (e) {
19653             nodeRange.selectNodeContents(node);
19654         }
19655     
19656         var rangeStartRange = range.cloneRange();
19657         rangeStartRange.collapse(true);
19658     
19659         var rangeEndRange = range.cloneRange();
19660         rangeEndRange.collapse(false);
19661     
19662         var nodeStartRange = nodeRange.cloneRange();
19663         nodeStartRange.collapse(true);
19664     
19665         var nodeEndRange = nodeRange.cloneRange();
19666         nodeEndRange.collapse(false);
19667     
19668         return rangeStartRange.compareBoundaryPoints(
19669                  Range.START_TO_START, nodeEndRange) == -1 &&
19670                rangeEndRange.compareBoundaryPoints(
19671                  Range.START_TO_START, nodeStartRange) == 1;
19672         
19673          
19674     },
19675     rangeCompareNode : function(range, node)
19676     {
19677         var nodeRange = node.ownerDocument.createRange();
19678         try {
19679             nodeRange.selectNode(node);
19680         } catch (e) {
19681             nodeRange.selectNodeContents(node);
19682         }
19683         
19684         
19685         range.collapse(true);
19686     
19687         nodeRange.collapse(true);
19688      
19689         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19690         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19691          
19692         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19693         
19694         var nodeIsBefore   =  ss == 1;
19695         var nodeIsAfter    = ee == -1;
19696         
19697         if (nodeIsBefore && nodeIsAfter)
19698             return 0; // outer
19699         if (!nodeIsBefore && nodeIsAfter)
19700             return 1; //right trailed.
19701         
19702         if (nodeIsBefore && !nodeIsAfter)
19703             return 2;  // left trailed.
19704         // fully contined.
19705         return 3;
19706     },
19707
19708     // private? - in a new class?
19709     cleanUpPaste :  function()
19710     {
19711         // cleans up the whole document..
19712         Roo.log('cleanuppaste');
19713         
19714         this.cleanUpChildren(this.doc.body);
19715         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19716         if (clean != this.doc.body.innerHTML) {
19717             this.doc.body.innerHTML = clean;
19718         }
19719         
19720     },
19721     
19722     cleanWordChars : function(input) {// change the chars to hex code
19723         var he = Roo.HtmlEditorCore;
19724         
19725         var output = input;
19726         Roo.each(he.swapCodes, function(sw) { 
19727             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19728             
19729             output = output.replace(swapper, sw[1]);
19730         });
19731         
19732         return output;
19733     },
19734     
19735     
19736     cleanUpChildren : function (n)
19737     {
19738         if (!n.childNodes.length) {
19739             return;
19740         }
19741         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19742            this.cleanUpChild(n.childNodes[i]);
19743         }
19744     },
19745     
19746     
19747         
19748     
19749     cleanUpChild : function (node)
19750     {
19751         var ed = this;
19752         //console.log(node);
19753         if (node.nodeName == "#text") {
19754             // clean up silly Windows -- stuff?
19755             return; 
19756         }
19757         if (node.nodeName == "#comment") {
19758             node.parentNode.removeChild(node);
19759             // clean up silly Windows -- stuff?
19760             return; 
19761         }
19762         var lcname = node.tagName.toLowerCase();
19763         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19764         // whitelist of tags..
19765         
19766         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19767             // remove node.
19768             node.parentNode.removeChild(node);
19769             return;
19770             
19771         }
19772         
19773         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19774         
19775         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19776         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19777         
19778         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19779         //    remove_keep_children = true;
19780         //}
19781         
19782         if (remove_keep_children) {
19783             this.cleanUpChildren(node);
19784             // inserts everything just before this node...
19785             while (node.childNodes.length) {
19786                 var cn = node.childNodes[0];
19787                 node.removeChild(cn);
19788                 node.parentNode.insertBefore(cn, node);
19789             }
19790             node.parentNode.removeChild(node);
19791             return;
19792         }
19793         
19794         if (!node.attributes || !node.attributes.length) {
19795             this.cleanUpChildren(node);
19796             return;
19797         }
19798         
19799         function cleanAttr(n,v)
19800         {
19801             
19802             if (v.match(/^\./) || v.match(/^\//)) {
19803                 return;
19804             }
19805             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19806                 return;
19807             }
19808             if (v.match(/^#/)) {
19809                 return;
19810             }
19811 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19812             node.removeAttribute(n);
19813             
19814         }
19815         
19816         var cwhite = this.cwhite;
19817         var cblack = this.cblack;
19818             
19819         function cleanStyle(n,v)
19820         {
19821             if (v.match(/expression/)) { //XSS?? should we even bother..
19822                 node.removeAttribute(n);
19823                 return;
19824             }
19825             
19826             var parts = v.split(/;/);
19827             var clean = [];
19828             
19829             Roo.each(parts, function(p) {
19830                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19831                 if (!p.length) {
19832                     return true;
19833                 }
19834                 var l = p.split(':').shift().replace(/\s+/g,'');
19835                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19836                 
19837                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19838 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19839                     //node.removeAttribute(n);
19840                     return true;
19841                 }
19842                 //Roo.log()
19843                 // only allow 'c whitelisted system attributes'
19844                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19845 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19846                     //node.removeAttribute(n);
19847                     return true;
19848                 }
19849                 
19850                 
19851                  
19852                 
19853                 clean.push(p);
19854                 return true;
19855             });
19856             if (clean.length) { 
19857                 node.setAttribute(n, clean.join(';'));
19858             } else {
19859                 node.removeAttribute(n);
19860             }
19861             
19862         }
19863         
19864         
19865         for (var i = node.attributes.length-1; i > -1 ; i--) {
19866             var a = node.attributes[i];
19867             //console.log(a);
19868             
19869             if (a.name.toLowerCase().substr(0,2)=='on')  {
19870                 node.removeAttribute(a.name);
19871                 continue;
19872             }
19873             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19874                 node.removeAttribute(a.name);
19875                 continue;
19876             }
19877             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19878                 cleanAttr(a.name,a.value); // fixme..
19879                 continue;
19880             }
19881             if (a.name == 'style') {
19882                 cleanStyle(a.name,a.value);
19883                 continue;
19884             }
19885             /// clean up MS crap..
19886             // tecnically this should be a list of valid class'es..
19887             
19888             
19889             if (a.name == 'class') {
19890                 if (a.value.match(/^Mso/)) {
19891                     node.className = '';
19892                 }
19893                 
19894                 if (a.value.match(/body/)) {
19895                     node.className = '';
19896                 }
19897                 continue;
19898             }
19899             
19900             // style cleanup!?
19901             // class cleanup?
19902             
19903         }
19904         
19905         
19906         this.cleanUpChildren(node);
19907         
19908         
19909     },
19910     
19911     /**
19912      * Clean up MS wordisms...
19913      */
19914     cleanWord : function(node)
19915     {
19916         
19917         
19918         if (!node) {
19919             this.cleanWord(this.doc.body);
19920             return;
19921         }
19922         if (node.nodeName == "#text") {
19923             // clean up silly Windows -- stuff?
19924             return; 
19925         }
19926         if (node.nodeName == "#comment") {
19927             node.parentNode.removeChild(node);
19928             // clean up silly Windows -- stuff?
19929             return; 
19930         }
19931         
19932         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19933             node.parentNode.removeChild(node);
19934             return;
19935         }
19936         
19937         // remove - but keep children..
19938         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19939             while (node.childNodes.length) {
19940                 var cn = node.childNodes[0];
19941                 node.removeChild(cn);
19942                 node.parentNode.insertBefore(cn, node);
19943             }
19944             node.parentNode.removeChild(node);
19945             this.iterateChildren(node, this.cleanWord);
19946             return;
19947         }
19948         // clean styles
19949         if (node.className.length) {
19950             
19951             var cn = node.className.split(/\W+/);
19952             var cna = [];
19953             Roo.each(cn, function(cls) {
19954                 if (cls.match(/Mso[a-zA-Z]+/)) {
19955                     return;
19956                 }
19957                 cna.push(cls);
19958             });
19959             node.className = cna.length ? cna.join(' ') : '';
19960             if (!cna.length) {
19961                 node.removeAttribute("class");
19962             }
19963         }
19964         
19965         if (node.hasAttribute("lang")) {
19966             node.removeAttribute("lang");
19967         }
19968         
19969         if (node.hasAttribute("style")) {
19970             
19971             var styles = node.getAttribute("style").split(";");
19972             var nstyle = [];
19973             Roo.each(styles, function(s) {
19974                 if (!s.match(/:/)) {
19975                     return;
19976                 }
19977                 var kv = s.split(":");
19978                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19979                     return;
19980                 }
19981                 // what ever is left... we allow.
19982                 nstyle.push(s);
19983             });
19984             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19985             if (!nstyle.length) {
19986                 node.removeAttribute('style');
19987             }
19988         }
19989         this.iterateChildren(node, this.cleanWord);
19990         
19991         
19992         
19993     },
19994     /**
19995      * iterateChildren of a Node, calling fn each time, using this as the scole..
19996      * @param {DomNode} node node to iterate children of.
19997      * @param {Function} fn method of this class to call on each item.
19998      */
19999     iterateChildren : function(node, fn)
20000     {
20001         if (!node.childNodes.length) {
20002                 return;
20003         }
20004         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20005            fn.call(this, node.childNodes[i])
20006         }
20007     },
20008     
20009     
20010     /**
20011      * cleanTableWidths.
20012      *
20013      * Quite often pasting from word etc.. results in tables with column and widths.
20014      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20015      *
20016      */
20017     cleanTableWidths : function(node)
20018     {
20019          
20020          
20021         if (!node) {
20022             this.cleanTableWidths(this.doc.body);
20023             return;
20024         }
20025         
20026         // ignore list...
20027         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20028             return; 
20029         }
20030         Roo.log(node.tagName);
20031         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20032             this.iterateChildren(node, this.cleanTableWidths);
20033             return;
20034         }
20035         if (node.hasAttribute('width')) {
20036             node.removeAttribute('width');
20037         }
20038         
20039          
20040         if (node.hasAttribute("style")) {
20041             // pretty basic...
20042             
20043             var styles = node.getAttribute("style").split(";");
20044             var nstyle = [];
20045             Roo.each(styles, function(s) {
20046                 if (!s.match(/:/)) {
20047                     return;
20048                 }
20049                 var kv = s.split(":");
20050                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20051                     return;
20052                 }
20053                 // what ever is left... we allow.
20054                 nstyle.push(s);
20055             });
20056             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20057             if (!nstyle.length) {
20058                 node.removeAttribute('style');
20059             }
20060         }
20061         
20062         this.iterateChildren(node, this.cleanTableWidths);
20063         
20064         
20065     },
20066     
20067     
20068     
20069     
20070     domToHTML : function(currentElement, depth, nopadtext) {
20071         
20072         depth = depth || 0;
20073         nopadtext = nopadtext || false;
20074     
20075         if (!currentElement) {
20076             return this.domToHTML(this.doc.body);
20077         }
20078         
20079         //Roo.log(currentElement);
20080         var j;
20081         var allText = false;
20082         var nodeName = currentElement.nodeName;
20083         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20084         
20085         if  (nodeName == '#text') {
20086             
20087             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20088         }
20089         
20090         
20091         var ret = '';
20092         if (nodeName != 'BODY') {
20093              
20094             var i = 0;
20095             // Prints the node tagName, such as <A>, <IMG>, etc
20096             if (tagName) {
20097                 var attr = [];
20098                 for(i = 0; i < currentElement.attributes.length;i++) {
20099                     // quoting?
20100                     var aname = currentElement.attributes.item(i).name;
20101                     if (!currentElement.attributes.item(i).value.length) {
20102                         continue;
20103                     }
20104                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20105                 }
20106                 
20107                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20108             } 
20109             else {
20110                 
20111                 // eack
20112             }
20113         } else {
20114             tagName = false;
20115         }
20116         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20117             return ret;
20118         }
20119         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20120             nopadtext = true;
20121         }
20122         
20123         
20124         // Traverse the tree
20125         i = 0;
20126         var currentElementChild = currentElement.childNodes.item(i);
20127         var allText = true;
20128         var innerHTML  = '';
20129         lastnode = '';
20130         while (currentElementChild) {
20131             // Formatting code (indent the tree so it looks nice on the screen)
20132             var nopad = nopadtext;
20133             if (lastnode == 'SPAN') {
20134                 nopad  = true;
20135             }
20136             // text
20137             if  (currentElementChild.nodeName == '#text') {
20138                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20139                 toadd = nopadtext ? toadd : toadd.trim();
20140                 if (!nopad && toadd.length > 80) {
20141                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20142                 }
20143                 innerHTML  += toadd;
20144                 
20145                 i++;
20146                 currentElementChild = currentElement.childNodes.item(i);
20147                 lastNode = '';
20148                 continue;
20149             }
20150             allText = false;
20151             
20152             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20153                 
20154             // Recursively traverse the tree structure of the child node
20155             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20156             lastnode = currentElementChild.nodeName;
20157             i++;
20158             currentElementChild=currentElement.childNodes.item(i);
20159         }
20160         
20161         ret += innerHTML;
20162         
20163         if (!allText) {
20164                 // The remaining code is mostly for formatting the tree
20165             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20166         }
20167         
20168         
20169         if (tagName) {
20170             ret+= "</"+tagName+">";
20171         }
20172         return ret;
20173         
20174     },
20175         
20176     applyBlacklists : function()
20177     {
20178         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20179         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20180         
20181         this.white = [];
20182         this.black = [];
20183         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20184             if (b.indexOf(tag) > -1) {
20185                 return;
20186             }
20187             this.white.push(tag);
20188             
20189         }, this);
20190         
20191         Roo.each(w, function(tag) {
20192             if (b.indexOf(tag) > -1) {
20193                 return;
20194             }
20195             if (this.white.indexOf(tag) > -1) {
20196                 return;
20197             }
20198             this.white.push(tag);
20199             
20200         }, this);
20201         
20202         
20203         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20204             if (w.indexOf(tag) > -1) {
20205                 return;
20206             }
20207             this.black.push(tag);
20208             
20209         }, this);
20210         
20211         Roo.each(b, function(tag) {
20212             if (w.indexOf(tag) > -1) {
20213                 return;
20214             }
20215             if (this.black.indexOf(tag) > -1) {
20216                 return;
20217             }
20218             this.black.push(tag);
20219             
20220         }, this);
20221         
20222         
20223         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20224         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20225         
20226         this.cwhite = [];
20227         this.cblack = [];
20228         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20229             if (b.indexOf(tag) > -1) {
20230                 return;
20231             }
20232             this.cwhite.push(tag);
20233             
20234         }, this);
20235         
20236         Roo.each(w, function(tag) {
20237             if (b.indexOf(tag) > -1) {
20238                 return;
20239             }
20240             if (this.cwhite.indexOf(tag) > -1) {
20241                 return;
20242             }
20243             this.cwhite.push(tag);
20244             
20245         }, this);
20246         
20247         
20248         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20249             if (w.indexOf(tag) > -1) {
20250                 return;
20251             }
20252             this.cblack.push(tag);
20253             
20254         }, this);
20255         
20256         Roo.each(b, function(tag) {
20257             if (w.indexOf(tag) > -1) {
20258                 return;
20259             }
20260             if (this.cblack.indexOf(tag) > -1) {
20261                 return;
20262             }
20263             this.cblack.push(tag);
20264             
20265         }, this);
20266     },
20267     
20268     setStylesheets : function(stylesheets)
20269     {
20270         if(typeof(stylesheets) == 'string'){
20271             Roo.get(this.iframe.contentDocument.head).createChild({
20272                 tag : 'link',
20273                 rel : 'stylesheet',
20274                 type : 'text/css',
20275                 href : stylesheets
20276             });
20277             
20278             return;
20279         }
20280         var _this = this;
20281      
20282         Roo.each(stylesheets, function(s) {
20283             if(!s.length){
20284                 return;
20285             }
20286             
20287             Roo.get(_this.iframe.contentDocument.head).createChild({
20288                 tag : 'link',
20289                 rel : 'stylesheet',
20290                 type : 'text/css',
20291                 href : s
20292             });
20293         });
20294
20295         
20296     },
20297     
20298     removeStylesheets : function()
20299     {
20300         var _this = this;
20301         
20302         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20303             s.remove();
20304         });
20305     }
20306     
20307     // hide stuff that is not compatible
20308     /**
20309      * @event blur
20310      * @hide
20311      */
20312     /**
20313      * @event change
20314      * @hide
20315      */
20316     /**
20317      * @event focus
20318      * @hide
20319      */
20320     /**
20321      * @event specialkey
20322      * @hide
20323      */
20324     /**
20325      * @cfg {String} fieldClass @hide
20326      */
20327     /**
20328      * @cfg {String} focusClass @hide
20329      */
20330     /**
20331      * @cfg {String} autoCreate @hide
20332      */
20333     /**
20334      * @cfg {String} inputType @hide
20335      */
20336     /**
20337      * @cfg {String} invalidClass @hide
20338      */
20339     /**
20340      * @cfg {String} invalidText @hide
20341      */
20342     /**
20343      * @cfg {String} msgFx @hide
20344      */
20345     /**
20346      * @cfg {String} validateOnBlur @hide
20347      */
20348 });
20349
20350 Roo.HtmlEditorCore.white = [
20351         'area', 'br', 'img', 'input', 'hr', 'wbr',
20352         
20353        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20354        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20355        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20356        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20357        'table',   'ul',         'xmp', 
20358        
20359        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20360       'thead',   'tr', 
20361      
20362       'dir', 'menu', 'ol', 'ul', 'dl',
20363        
20364       'embed',  'object'
20365 ];
20366
20367
20368 Roo.HtmlEditorCore.black = [
20369     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20370         'applet', // 
20371         'base',   'basefont', 'bgsound', 'blink',  'body', 
20372         'frame',  'frameset', 'head',    'html',   'ilayer', 
20373         'iframe', 'layer',  'link',     'meta',    'object',   
20374         'script', 'style' ,'title',  'xml' // clean later..
20375 ];
20376 Roo.HtmlEditorCore.clean = [
20377     'script', 'style', 'title', 'xml'
20378 ];
20379 Roo.HtmlEditorCore.remove = [
20380     'font'
20381 ];
20382 // attributes..
20383
20384 Roo.HtmlEditorCore.ablack = [
20385     'on'
20386 ];
20387     
20388 Roo.HtmlEditorCore.aclean = [ 
20389     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20390 ];
20391
20392 // protocols..
20393 Roo.HtmlEditorCore.pwhite= [
20394         'http',  'https',  'mailto'
20395 ];
20396
20397 // white listed style attributes.
20398 Roo.HtmlEditorCore.cwhite= [
20399       //  'text-align', /// default is to allow most things..
20400       
20401          
20402 //        'font-size'//??
20403 ];
20404
20405 // black listed style attributes.
20406 Roo.HtmlEditorCore.cblack= [
20407       //  'font-size' -- this can be set by the project 
20408 ];
20409
20410
20411 Roo.HtmlEditorCore.swapCodes   =[ 
20412     [    8211, "--" ], 
20413     [    8212, "--" ], 
20414     [    8216,  "'" ],  
20415     [    8217, "'" ],  
20416     [    8220, '"' ],  
20417     [    8221, '"' ],  
20418     [    8226, "*" ],  
20419     [    8230, "..." ]
20420 ]; 
20421
20422     /*
20423  * - LGPL
20424  *
20425  * HtmlEditor
20426  * 
20427  */
20428
20429 /**
20430  * @class Roo.bootstrap.HtmlEditor
20431  * @extends Roo.bootstrap.TextArea
20432  * Bootstrap HtmlEditor class
20433
20434  * @constructor
20435  * Create a new HtmlEditor
20436  * @param {Object} config The config object
20437  */
20438
20439 Roo.bootstrap.HtmlEditor = function(config){
20440     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20441     if (!this.toolbars) {
20442         this.toolbars = [];
20443     }
20444     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20445     this.addEvents({
20446             /**
20447              * @event initialize
20448              * Fires when the editor is fully initialized (including the iframe)
20449              * @param {HtmlEditor} this
20450              */
20451             initialize: true,
20452             /**
20453              * @event activate
20454              * Fires when the editor is first receives the focus. Any insertion must wait
20455              * until after this event.
20456              * @param {HtmlEditor} this
20457              */
20458             activate: true,
20459              /**
20460              * @event beforesync
20461              * Fires before the textarea is updated with content from the editor iframe. Return false
20462              * to cancel the sync.
20463              * @param {HtmlEditor} this
20464              * @param {String} html
20465              */
20466             beforesync: true,
20467              /**
20468              * @event beforepush
20469              * Fires before the iframe editor is updated with content from the textarea. Return false
20470              * to cancel the push.
20471              * @param {HtmlEditor} this
20472              * @param {String} html
20473              */
20474             beforepush: true,
20475              /**
20476              * @event sync
20477              * Fires when the textarea is updated with content from the editor iframe.
20478              * @param {HtmlEditor} this
20479              * @param {String} html
20480              */
20481             sync: true,
20482              /**
20483              * @event push
20484              * Fires when the iframe editor is updated with content from the textarea.
20485              * @param {HtmlEditor} this
20486              * @param {String} html
20487              */
20488             push: true,
20489              /**
20490              * @event editmodechange
20491              * Fires when the editor switches edit modes
20492              * @param {HtmlEditor} this
20493              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20494              */
20495             editmodechange: true,
20496             /**
20497              * @event editorevent
20498              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20499              * @param {HtmlEditor} this
20500              */
20501             editorevent: true,
20502             /**
20503              * @event firstfocus
20504              * Fires when on first focus - needed by toolbars..
20505              * @param {HtmlEditor} this
20506              */
20507             firstfocus: true,
20508             /**
20509              * @event autosave
20510              * Auto save the htmlEditor value as a file into Events
20511              * @param {HtmlEditor} this
20512              */
20513             autosave: true,
20514             /**
20515              * @event savedpreview
20516              * preview the saved version of htmlEditor
20517              * @param {HtmlEditor} this
20518              */
20519             savedpreview: true
20520         });
20521 };
20522
20523
20524 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20525     
20526     
20527       /**
20528      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20529      */
20530     toolbars : false,
20531    
20532      /**
20533      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20534      *                        Roo.resizable.
20535      */
20536     resizable : false,
20537      /**
20538      * @cfg {Number} height (in pixels)
20539      */   
20540     height: 300,
20541    /**
20542      * @cfg {Number} width (in pixels)
20543      */   
20544     width: false,
20545     
20546     /**
20547      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20548      * 
20549      */
20550     stylesheets: false,
20551     
20552     // id of frame..
20553     frameId: false,
20554     
20555     // private properties
20556     validationEvent : false,
20557     deferHeight: true,
20558     initialized : false,
20559     activated : false,
20560     
20561     onFocus : Roo.emptyFn,
20562     iframePad:3,
20563     hideMode:'offsets',
20564     
20565     
20566     tbContainer : false,
20567     
20568     toolbarContainer :function() {
20569         return this.wrap.select('.x-html-editor-tb',true).first();
20570     },
20571
20572     /**
20573      * Protected method that will not generally be called directly. It
20574      * is called when the editor creates its toolbar. Override this method if you need to
20575      * add custom toolbar buttons.
20576      * @param {HtmlEditor} editor
20577      */
20578     createToolbar : function(){
20579         
20580         Roo.log("create toolbars");
20581         
20582         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20583         this.toolbars[0].render(this.toolbarContainer());
20584         
20585         return;
20586         
20587 //        if (!editor.toolbars || !editor.toolbars.length) {
20588 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20589 //        }
20590 //        
20591 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20592 //            editor.toolbars[i] = Roo.factory(
20593 //                    typeof(editor.toolbars[i]) == 'string' ?
20594 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20595 //                Roo.bootstrap.HtmlEditor);
20596 //            editor.toolbars[i].init(editor);
20597 //        }
20598     },
20599
20600      
20601     // private
20602     onRender : function(ct, position)
20603     {
20604        // Roo.log("Call onRender: " + this.xtype);
20605         var _t = this;
20606         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20607       
20608         this.wrap = this.inputEl().wrap({
20609             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20610         });
20611         
20612         this.editorcore.onRender(ct, position);
20613          
20614         if (this.resizable) {
20615             this.resizeEl = new Roo.Resizable(this.wrap, {
20616                 pinned : true,
20617                 wrap: true,
20618                 dynamic : true,
20619                 minHeight : this.height,
20620                 height: this.height,
20621                 handles : this.resizable,
20622                 width: this.width,
20623                 listeners : {
20624                     resize : function(r, w, h) {
20625                         _t.onResize(w,h); // -something
20626                     }
20627                 }
20628             });
20629             
20630         }
20631         this.createToolbar(this);
20632        
20633         
20634         if(!this.width && this.resizable){
20635             this.setSize(this.wrap.getSize());
20636         }
20637         if (this.resizeEl) {
20638             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20639             // should trigger onReize..
20640         }
20641         
20642     },
20643
20644     // private
20645     onResize : function(w, h)
20646     {
20647         Roo.log('resize: ' +w + ',' + h );
20648         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20649         var ew = false;
20650         var eh = false;
20651         
20652         if(this.inputEl() ){
20653             if(typeof w == 'number'){
20654                 var aw = w - this.wrap.getFrameWidth('lr');
20655                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20656                 ew = aw;
20657             }
20658             if(typeof h == 'number'){
20659                  var tbh = -11;  // fixme it needs to tool bar size!
20660                 for (var i =0; i < this.toolbars.length;i++) {
20661                     // fixme - ask toolbars for heights?
20662                     tbh += this.toolbars[i].el.getHeight();
20663                     //if (this.toolbars[i].footer) {
20664                     //    tbh += this.toolbars[i].footer.el.getHeight();
20665                     //}
20666                 }
20667               
20668                 
20669                 
20670                 
20671                 
20672                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20673                 ah -= 5; // knock a few pixes off for look..
20674                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20675                 var eh = ah;
20676             }
20677         }
20678         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20679         this.editorcore.onResize(ew,eh);
20680         
20681     },
20682
20683     /**
20684      * Toggles the editor between standard and source edit mode.
20685      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20686      */
20687     toggleSourceEdit : function(sourceEditMode)
20688     {
20689         this.editorcore.toggleSourceEdit(sourceEditMode);
20690         
20691         if(this.editorcore.sourceEditMode){
20692             Roo.log('editor - showing textarea');
20693             
20694 //            Roo.log('in');
20695 //            Roo.log(this.syncValue());
20696             this.syncValue();
20697             this.inputEl().removeClass(['hide', 'x-hidden']);
20698             this.inputEl().dom.removeAttribute('tabIndex');
20699             this.inputEl().focus();
20700         }else{
20701             Roo.log('editor - hiding textarea');
20702 //            Roo.log('out')
20703 //            Roo.log(this.pushValue()); 
20704             this.pushValue();
20705             
20706             this.inputEl().addClass(['hide', 'x-hidden']);
20707             this.inputEl().dom.setAttribute('tabIndex', -1);
20708             //this.deferFocus();
20709         }
20710          
20711         if(this.resizable){
20712             this.setSize(this.wrap.getSize());
20713         }
20714         
20715         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20716     },
20717  
20718     // private (for BoxComponent)
20719     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20720
20721     // private (for BoxComponent)
20722     getResizeEl : function(){
20723         return this.wrap;
20724     },
20725
20726     // private (for BoxComponent)
20727     getPositionEl : function(){
20728         return this.wrap;
20729     },
20730
20731     // private
20732     initEvents : function(){
20733         this.originalValue = this.getValue();
20734     },
20735
20736 //    /**
20737 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20738 //     * @method
20739 //     */
20740 //    markInvalid : Roo.emptyFn,
20741 //    /**
20742 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20743 //     * @method
20744 //     */
20745 //    clearInvalid : Roo.emptyFn,
20746
20747     setValue : function(v){
20748         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20749         this.editorcore.pushValue();
20750     },
20751
20752      
20753     // private
20754     deferFocus : function(){
20755         this.focus.defer(10, this);
20756     },
20757
20758     // doc'ed in Field
20759     focus : function(){
20760         this.editorcore.focus();
20761         
20762     },
20763       
20764
20765     // private
20766     onDestroy : function(){
20767         
20768         
20769         
20770         if(this.rendered){
20771             
20772             for (var i =0; i < this.toolbars.length;i++) {
20773                 // fixme - ask toolbars for heights?
20774                 this.toolbars[i].onDestroy();
20775             }
20776             
20777             this.wrap.dom.innerHTML = '';
20778             this.wrap.remove();
20779         }
20780     },
20781
20782     // private
20783     onFirstFocus : function(){
20784         //Roo.log("onFirstFocus");
20785         this.editorcore.onFirstFocus();
20786          for (var i =0; i < this.toolbars.length;i++) {
20787             this.toolbars[i].onFirstFocus();
20788         }
20789         
20790     },
20791     
20792     // private
20793     syncValue : function()
20794     {   
20795         this.editorcore.syncValue();
20796     },
20797     
20798     pushValue : function()
20799     {   
20800         this.editorcore.pushValue();
20801     }
20802      
20803     
20804     // hide stuff that is not compatible
20805     /**
20806      * @event blur
20807      * @hide
20808      */
20809     /**
20810      * @event change
20811      * @hide
20812      */
20813     /**
20814      * @event focus
20815      * @hide
20816      */
20817     /**
20818      * @event specialkey
20819      * @hide
20820      */
20821     /**
20822      * @cfg {String} fieldClass @hide
20823      */
20824     /**
20825      * @cfg {String} focusClass @hide
20826      */
20827     /**
20828      * @cfg {String} autoCreate @hide
20829      */
20830     /**
20831      * @cfg {String} inputType @hide
20832      */
20833     /**
20834      * @cfg {String} invalidClass @hide
20835      */
20836     /**
20837      * @cfg {String} invalidText @hide
20838      */
20839     /**
20840      * @cfg {String} msgFx @hide
20841      */
20842     /**
20843      * @cfg {String} validateOnBlur @hide
20844      */
20845 });
20846  
20847     
20848    
20849    
20850    
20851       
20852 Roo.namespace('Roo.bootstrap.htmleditor');
20853 /**
20854  * @class Roo.bootstrap.HtmlEditorToolbar1
20855  * Basic Toolbar
20856  * 
20857  * Usage:
20858  *
20859  new Roo.bootstrap.HtmlEditor({
20860     ....
20861     toolbars : [
20862         new Roo.bootstrap.HtmlEditorToolbar1({
20863             disable : { fonts: 1 , format: 1, ..., ... , ...],
20864             btns : [ .... ]
20865         })
20866     }
20867      
20868  * 
20869  * @cfg {Object} disable List of elements to disable..
20870  * @cfg {Array} btns List of additional buttons.
20871  * 
20872  * 
20873  * NEEDS Extra CSS? 
20874  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20875  */
20876  
20877 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20878 {
20879     
20880     Roo.apply(this, config);
20881     
20882     // default disabled, based on 'good practice'..
20883     this.disable = this.disable || {};
20884     Roo.applyIf(this.disable, {
20885         fontSize : true,
20886         colors : true,
20887         specialElements : true
20888     });
20889     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20890     
20891     this.editor = config.editor;
20892     this.editorcore = config.editor.editorcore;
20893     
20894     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20895     
20896     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20897     // dont call parent... till later.
20898 }
20899 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20900      
20901     bar : true,
20902     
20903     editor : false,
20904     editorcore : false,
20905     
20906     
20907     formats : [
20908         "p" ,  
20909         "h1","h2","h3","h4","h5","h6", 
20910         "pre", "code", 
20911         "abbr", "acronym", "address", "cite", "samp", "var",
20912         'div','span'
20913     ],
20914     
20915     onRender : function(ct, position)
20916     {
20917        // Roo.log("Call onRender: " + this.xtype);
20918         
20919        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20920        Roo.log(this.el);
20921        this.el.dom.style.marginBottom = '0';
20922        var _this = this;
20923        var editorcore = this.editorcore;
20924        var editor= this.editor;
20925        
20926        var children = [];
20927        var btn = function(id,cmd , toggle, handler){
20928        
20929             var  event = toggle ? 'toggle' : 'click';
20930        
20931             var a = {
20932                 size : 'sm',
20933                 xtype: 'Button',
20934                 xns: Roo.bootstrap,
20935                 glyphicon : id,
20936                 cmd : id || cmd,
20937                 enableToggle:toggle !== false,
20938                 //html : 'submit'
20939                 pressed : toggle ? false : null,
20940                 listeners : {}
20941             };
20942             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20943                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20944             };
20945             children.push(a);
20946             return a;
20947        }
20948         
20949         var style = {
20950                 xtype: 'Button',
20951                 size : 'sm',
20952                 xns: Roo.bootstrap,
20953                 glyphicon : 'font',
20954                 //html : 'submit'
20955                 menu : {
20956                     xtype: 'Menu',
20957                     xns: Roo.bootstrap,
20958                     items:  []
20959                 }
20960         };
20961         Roo.each(this.formats, function(f) {
20962             style.menu.items.push({
20963                 xtype :'MenuItem',
20964                 xns: Roo.bootstrap,
20965                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20966                 tagname : f,
20967                 listeners : {
20968                     click : function()
20969                     {
20970                         editorcore.insertTag(this.tagname);
20971                         editor.focus();
20972                     }
20973                 }
20974                 
20975             });
20976         });
20977          children.push(style);   
20978             
20979             
20980         btn('bold',false,true);
20981         btn('italic',false,true);
20982         btn('align-left', 'justifyleft',true);
20983         btn('align-center', 'justifycenter',true);
20984         btn('align-right' , 'justifyright',true);
20985         btn('link', false, false, function(btn) {
20986             //Roo.log("create link?");
20987             var url = prompt(this.createLinkText, this.defaultLinkValue);
20988             if(url && url != 'http:/'+'/'){
20989                 this.editorcore.relayCmd('createlink', url);
20990             }
20991         }),
20992         btn('list','insertunorderedlist',true);
20993         btn('pencil', false,true, function(btn){
20994                 Roo.log(this);
20995                 
20996                 this.toggleSourceEdit(btn.pressed);
20997         });
20998         /*
20999         var cog = {
21000                 xtype: 'Button',
21001                 size : 'sm',
21002                 xns: Roo.bootstrap,
21003                 glyphicon : 'cog',
21004                 //html : 'submit'
21005                 menu : {
21006                     xtype: 'Menu',
21007                     xns: Roo.bootstrap,
21008                     items:  []
21009                 }
21010         };
21011         
21012         cog.menu.items.push({
21013             xtype :'MenuItem',
21014             xns: Roo.bootstrap,
21015             html : Clean styles,
21016             tagname : f,
21017             listeners : {
21018                 click : function()
21019                 {
21020                     editorcore.insertTag(this.tagname);
21021                     editor.focus();
21022                 }
21023             }
21024             
21025         });
21026        */
21027         
21028          
21029        this.xtype = 'NavSimplebar';
21030         
21031         for(var i=0;i< children.length;i++) {
21032             
21033             this.buttons.add(this.addxtypeChild(children[i]));
21034             
21035         }
21036         
21037         editor.on('editorevent', this.updateToolbar, this);
21038     },
21039     onBtnClick : function(id)
21040     {
21041        this.editorcore.relayCmd(id);
21042        this.editorcore.focus();
21043     },
21044     
21045     /**
21046      * Protected method that will not generally be called directly. It triggers
21047      * a toolbar update by reading the markup state of the current selection in the editor.
21048      */
21049     updateToolbar: function(){
21050
21051         if(!this.editorcore.activated){
21052             this.editor.onFirstFocus(); // is this neeed?
21053             return;
21054         }
21055
21056         var btns = this.buttons; 
21057         var doc = this.editorcore.doc;
21058         btns.get('bold').setActive(doc.queryCommandState('bold'));
21059         btns.get('italic').setActive(doc.queryCommandState('italic'));
21060         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21061         
21062         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21063         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21064         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21065         
21066         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21067         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21068          /*
21069         
21070         var ans = this.editorcore.getAllAncestors();
21071         if (this.formatCombo) {
21072             
21073             
21074             var store = this.formatCombo.store;
21075             this.formatCombo.setValue("");
21076             for (var i =0; i < ans.length;i++) {
21077                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21078                     // select it..
21079                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21080                     break;
21081                 }
21082             }
21083         }
21084         
21085         
21086         
21087         // hides menus... - so this cant be on a menu...
21088         Roo.bootstrap.MenuMgr.hideAll();
21089         */
21090         Roo.bootstrap.MenuMgr.hideAll();
21091         //this.editorsyncValue();
21092     },
21093     onFirstFocus: function() {
21094         this.buttons.each(function(item){
21095            item.enable();
21096         });
21097     },
21098     toggleSourceEdit : function(sourceEditMode){
21099         
21100           
21101         if(sourceEditMode){
21102             Roo.log("disabling buttons");
21103            this.buttons.each( function(item){
21104                 if(item.cmd != 'pencil'){
21105                     item.disable();
21106                 }
21107             });
21108           
21109         }else{
21110             Roo.log("enabling buttons");
21111             if(this.editorcore.initialized){
21112                 this.buttons.each( function(item){
21113                     item.enable();
21114                 });
21115             }
21116             
21117         }
21118         Roo.log("calling toggole on editor");
21119         // tell the editor that it's been pressed..
21120         this.editor.toggleSourceEdit(sourceEditMode);
21121        
21122     }
21123 });
21124
21125
21126
21127
21128
21129 /**
21130  * @class Roo.bootstrap.Table.AbstractSelectionModel
21131  * @extends Roo.util.Observable
21132  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21133  * implemented by descendant classes.  This class should not be directly instantiated.
21134  * @constructor
21135  */
21136 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21137     this.locked = false;
21138     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21139 };
21140
21141
21142 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21143     /** @ignore Called by the grid automatically. Do not call directly. */
21144     init : function(grid){
21145         this.grid = grid;
21146         this.initEvents();
21147     },
21148
21149     /**
21150      * Locks the selections.
21151      */
21152     lock : function(){
21153         this.locked = true;
21154     },
21155
21156     /**
21157      * Unlocks the selections.
21158      */
21159     unlock : function(){
21160         this.locked = false;
21161     },
21162
21163     /**
21164      * Returns true if the selections are locked.
21165      * @return {Boolean}
21166      */
21167     isLocked : function(){
21168         return this.locked;
21169     }
21170 });
21171 /**
21172  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21173  * @class Roo.bootstrap.Table.RowSelectionModel
21174  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21175  * It supports multiple selections and keyboard selection/navigation. 
21176  * @constructor
21177  * @param {Object} config
21178  */
21179
21180 Roo.bootstrap.Table.RowSelectionModel = function(config){
21181     Roo.apply(this, config);
21182     this.selections = new Roo.util.MixedCollection(false, function(o){
21183         return o.id;
21184     });
21185
21186     this.last = false;
21187     this.lastActive = false;
21188
21189     this.addEvents({
21190         /**
21191              * @event selectionchange
21192              * Fires when the selection changes
21193              * @param {SelectionModel} this
21194              */
21195             "selectionchange" : true,
21196         /**
21197              * @event afterselectionchange
21198              * Fires after the selection changes (eg. by key press or clicking)
21199              * @param {SelectionModel} this
21200              */
21201             "afterselectionchange" : true,
21202         /**
21203              * @event beforerowselect
21204              * Fires when a row is selected being selected, return false to cancel.
21205              * @param {SelectionModel} this
21206              * @param {Number} rowIndex The selected index
21207              * @param {Boolean} keepExisting False if other selections will be cleared
21208              */
21209             "beforerowselect" : true,
21210         /**
21211              * @event rowselect
21212              * Fires when a row is selected.
21213              * @param {SelectionModel} this
21214              * @param {Number} rowIndex The selected index
21215              * @param {Roo.data.Record} r The record
21216              */
21217             "rowselect" : true,
21218         /**
21219              * @event rowdeselect
21220              * Fires when a row is deselected.
21221              * @param {SelectionModel} this
21222              * @param {Number} rowIndex The selected index
21223              */
21224         "rowdeselect" : true
21225     });
21226     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21227     this.locked = false;
21228 };
21229
21230 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21231     /**
21232      * @cfg {Boolean} singleSelect
21233      * True to allow selection of only one row at a time (defaults to false)
21234      */
21235     singleSelect : false,
21236
21237     // private
21238     initEvents : function(){
21239
21240         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21241             this.grid.on("mousedown", this.handleMouseDown, this);
21242         }else{ // allow click to work like normal
21243             this.grid.on("rowclick", this.handleDragableRowClick, this);
21244         }
21245
21246         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21247             "up" : function(e){
21248                 if(!e.shiftKey){
21249                     this.selectPrevious(e.shiftKey);
21250                 }else if(this.last !== false && this.lastActive !== false){
21251                     var last = this.last;
21252                     this.selectRange(this.last,  this.lastActive-1);
21253                     this.grid.getView().focusRow(this.lastActive);
21254                     if(last !== false){
21255                         this.last = last;
21256                     }
21257                 }else{
21258                     this.selectFirstRow();
21259                 }
21260                 this.fireEvent("afterselectionchange", this);
21261             },
21262             "down" : function(e){
21263                 if(!e.shiftKey){
21264                     this.selectNext(e.shiftKey);
21265                 }else if(this.last !== false && this.lastActive !== false){
21266                     var last = this.last;
21267                     this.selectRange(this.last,  this.lastActive+1);
21268                     this.grid.getView().focusRow(this.lastActive);
21269                     if(last !== false){
21270                         this.last = last;
21271                     }
21272                 }else{
21273                     this.selectFirstRow();
21274                 }
21275                 this.fireEvent("afterselectionchange", this);
21276             },
21277             scope: this
21278         });
21279
21280         var view = this.grid.view;
21281         view.on("refresh", this.onRefresh, this);
21282         view.on("rowupdated", this.onRowUpdated, this);
21283         view.on("rowremoved", this.onRemove, this);
21284     },
21285
21286     // private
21287     onRefresh : function(){
21288         var ds = this.grid.dataSource, i, v = this.grid.view;
21289         var s = this.selections;
21290         s.each(function(r){
21291             if((i = ds.indexOfId(r.id)) != -1){
21292                 v.onRowSelect(i);
21293             }else{
21294                 s.remove(r);
21295             }
21296         });
21297     },
21298
21299     // private
21300     onRemove : function(v, index, r){
21301         this.selections.remove(r);
21302     },
21303
21304     // private
21305     onRowUpdated : function(v, index, r){
21306         if(this.isSelected(r)){
21307             v.onRowSelect(index);
21308         }
21309     },
21310
21311     /**
21312      * Select records.
21313      * @param {Array} records The records to select
21314      * @param {Boolean} keepExisting (optional) True to keep existing selections
21315      */
21316     selectRecords : function(records, keepExisting){
21317         if(!keepExisting){
21318             this.clearSelections();
21319         }
21320         var ds = this.grid.dataSource;
21321         for(var i = 0, len = records.length; i < len; i++){
21322             this.selectRow(ds.indexOf(records[i]), true);
21323         }
21324     },
21325
21326     /**
21327      * Gets the number of selected rows.
21328      * @return {Number}
21329      */
21330     getCount : function(){
21331         return this.selections.length;
21332     },
21333
21334     /**
21335      * Selects the first row in the grid.
21336      */
21337     selectFirstRow : function(){
21338         this.selectRow(0);
21339     },
21340
21341     /**
21342      * Select the last row.
21343      * @param {Boolean} keepExisting (optional) True to keep existing selections
21344      */
21345     selectLastRow : function(keepExisting){
21346         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21347     },
21348
21349     /**
21350      * Selects the row immediately following the last selected row.
21351      * @param {Boolean} keepExisting (optional) True to keep existing selections
21352      */
21353     selectNext : function(keepExisting){
21354         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21355             this.selectRow(this.last+1, keepExisting);
21356             this.grid.getView().focusRow(this.last);
21357         }
21358     },
21359
21360     /**
21361      * Selects the row that precedes the last selected row.
21362      * @param {Boolean} keepExisting (optional) True to keep existing selections
21363      */
21364     selectPrevious : function(keepExisting){
21365         if(this.last){
21366             this.selectRow(this.last-1, keepExisting);
21367             this.grid.getView().focusRow(this.last);
21368         }
21369     },
21370
21371     /**
21372      * Returns the selected records
21373      * @return {Array} Array of selected records
21374      */
21375     getSelections : function(){
21376         return [].concat(this.selections.items);
21377     },
21378
21379     /**
21380      * Returns the first selected record.
21381      * @return {Record}
21382      */
21383     getSelected : function(){
21384         return this.selections.itemAt(0);
21385     },
21386
21387
21388     /**
21389      * Clears all selections.
21390      */
21391     clearSelections : function(fast){
21392         if(this.locked) return;
21393         if(fast !== true){
21394             var ds = this.grid.dataSource;
21395             var s = this.selections;
21396             s.each(function(r){
21397                 this.deselectRow(ds.indexOfId(r.id));
21398             }, this);
21399             s.clear();
21400         }else{
21401             this.selections.clear();
21402         }
21403         this.last = false;
21404     },
21405
21406
21407     /**
21408      * Selects all rows.
21409      */
21410     selectAll : function(){
21411         if(this.locked) return;
21412         this.selections.clear();
21413         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21414             this.selectRow(i, true);
21415         }
21416     },
21417
21418     /**
21419      * Returns True if there is a selection.
21420      * @return {Boolean}
21421      */
21422     hasSelection : function(){
21423         return this.selections.length > 0;
21424     },
21425
21426     /**
21427      * Returns True if the specified row is selected.
21428      * @param {Number/Record} record The record or index of the record to check
21429      * @return {Boolean}
21430      */
21431     isSelected : function(index){
21432         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21433         return (r && this.selections.key(r.id) ? true : false);
21434     },
21435
21436     /**
21437      * Returns True if the specified record id is selected.
21438      * @param {String} id The id of record to check
21439      * @return {Boolean}
21440      */
21441     isIdSelected : function(id){
21442         return (this.selections.key(id) ? true : false);
21443     },
21444
21445     // private
21446     handleMouseDown : function(e, t){
21447         var view = this.grid.getView(), rowIndex;
21448         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21449             return;
21450         };
21451         if(e.shiftKey && this.last !== false){
21452             var last = this.last;
21453             this.selectRange(last, rowIndex, e.ctrlKey);
21454             this.last = last; // reset the last
21455             view.focusRow(rowIndex);
21456         }else{
21457             var isSelected = this.isSelected(rowIndex);
21458             if(e.button !== 0 && isSelected){
21459                 view.focusRow(rowIndex);
21460             }else if(e.ctrlKey && isSelected){
21461                 this.deselectRow(rowIndex);
21462             }else if(!isSelected){
21463                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21464                 view.focusRow(rowIndex);
21465             }
21466         }
21467         this.fireEvent("afterselectionchange", this);
21468     },
21469     // private
21470     handleDragableRowClick :  function(grid, rowIndex, e) 
21471     {
21472         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21473             this.selectRow(rowIndex, false);
21474             grid.view.focusRow(rowIndex);
21475              this.fireEvent("afterselectionchange", this);
21476         }
21477     },
21478     
21479     /**
21480      * Selects multiple rows.
21481      * @param {Array} rows Array of the indexes of the row to select
21482      * @param {Boolean} keepExisting (optional) True to keep existing selections
21483      */
21484     selectRows : function(rows, keepExisting){
21485         if(!keepExisting){
21486             this.clearSelections();
21487         }
21488         for(var i = 0, len = rows.length; i < len; i++){
21489             this.selectRow(rows[i], true);
21490         }
21491     },
21492
21493     /**
21494      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21495      * @param {Number} startRow The index of the first row in the range
21496      * @param {Number} endRow The index of the last row in the range
21497      * @param {Boolean} keepExisting (optional) True to retain existing selections
21498      */
21499     selectRange : function(startRow, endRow, keepExisting){
21500         if(this.locked) return;
21501         if(!keepExisting){
21502             this.clearSelections();
21503         }
21504         if(startRow <= endRow){
21505             for(var i = startRow; i <= endRow; i++){
21506                 this.selectRow(i, true);
21507             }
21508         }else{
21509             for(var i = startRow; i >= endRow; i--){
21510                 this.selectRow(i, true);
21511             }
21512         }
21513     },
21514
21515     /**
21516      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21517      * @param {Number} startRow The index of the first row in the range
21518      * @param {Number} endRow The index of the last row in the range
21519      */
21520     deselectRange : function(startRow, endRow, preventViewNotify){
21521         if(this.locked) return;
21522         for(var i = startRow; i <= endRow; i++){
21523             this.deselectRow(i, preventViewNotify);
21524         }
21525     },
21526
21527     /**
21528      * Selects a row.
21529      * @param {Number} row The index of the row to select
21530      * @param {Boolean} keepExisting (optional) True to keep existing selections
21531      */
21532     selectRow : function(index, keepExisting, preventViewNotify){
21533         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21534         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21535             if(!keepExisting || this.singleSelect){
21536                 this.clearSelections();
21537             }
21538             var r = this.grid.dataSource.getAt(index);
21539             this.selections.add(r);
21540             this.last = this.lastActive = index;
21541             if(!preventViewNotify){
21542                 this.grid.getView().onRowSelect(index);
21543             }
21544             this.fireEvent("rowselect", this, index, r);
21545             this.fireEvent("selectionchange", this);
21546         }
21547     },
21548
21549     /**
21550      * Deselects a row.
21551      * @param {Number} row The index of the row to deselect
21552      */
21553     deselectRow : function(index, preventViewNotify){
21554         if(this.locked) return;
21555         if(this.last == index){
21556             this.last = false;
21557         }
21558         if(this.lastActive == index){
21559             this.lastActive = false;
21560         }
21561         var r = this.grid.dataSource.getAt(index);
21562         this.selections.remove(r);
21563         if(!preventViewNotify){
21564             this.grid.getView().onRowDeselect(index);
21565         }
21566         this.fireEvent("rowdeselect", this, index);
21567         this.fireEvent("selectionchange", this);
21568     },
21569
21570     // private
21571     restoreLast : function(){
21572         if(this._last){
21573             this.last = this._last;
21574         }
21575     },
21576
21577     // private
21578     acceptsNav : function(row, col, cm){
21579         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21580     },
21581
21582     // private
21583     onEditorKey : function(field, e){
21584         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21585         if(k == e.TAB){
21586             e.stopEvent();
21587             ed.completeEdit();
21588             if(e.shiftKey){
21589                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21590             }else{
21591                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21592             }
21593         }else if(k == e.ENTER && !e.ctrlKey){
21594             e.stopEvent();
21595             ed.completeEdit();
21596             if(e.shiftKey){
21597                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21598             }else{
21599                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21600             }
21601         }else if(k == e.ESC){
21602             ed.cancelEdit();
21603         }
21604         if(newCell){
21605             g.startEditing(newCell[0], newCell[1]);
21606         }
21607     }
21608 });/*
21609  * Based on:
21610  * Ext JS Library 1.1.1
21611  * Copyright(c) 2006-2007, Ext JS, LLC.
21612  *
21613  * Originally Released Under LGPL - original licence link has changed is not relivant.
21614  *
21615  * Fork - LGPL
21616  * <script type="text/javascript">
21617  */
21618  
21619 /**
21620  * @class Roo.bootstrap.PagingToolbar
21621  * @extends Roo.Row
21622  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21623  * @constructor
21624  * Create a new PagingToolbar
21625  * @param {Object} config The config object
21626  */
21627 Roo.bootstrap.PagingToolbar = function(config)
21628 {
21629     // old args format still supported... - xtype is prefered..
21630         // created from xtype...
21631     var ds = config.dataSource;
21632     this.toolbarItems = [];
21633     if (config.items) {
21634         this.toolbarItems = config.items;
21635 //        config.items = [];
21636     }
21637     
21638     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21639     this.ds = ds;
21640     this.cursor = 0;
21641     if (ds) { 
21642         this.bind(ds);
21643     }
21644     
21645     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21646     
21647 };
21648
21649 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21650     /**
21651      * @cfg {Roo.data.Store} dataSource
21652      * The underlying data store providing the paged data
21653      */
21654     /**
21655      * @cfg {String/HTMLElement/Element} container
21656      * container The id or element that will contain the toolbar
21657      */
21658     /**
21659      * @cfg {Boolean} displayInfo
21660      * True to display the displayMsg (defaults to false)
21661      */
21662     /**
21663      * @cfg {Number} pageSize
21664      * The number of records to display per page (defaults to 20)
21665      */
21666     pageSize: 20,
21667     /**
21668      * @cfg {String} displayMsg
21669      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21670      */
21671     displayMsg : 'Displaying {0} - {1} of {2}',
21672     /**
21673      * @cfg {String} emptyMsg
21674      * The message to display when no records are found (defaults to "No data to display")
21675      */
21676     emptyMsg : 'No data to display',
21677     /**
21678      * Customizable piece of the default paging text (defaults to "Page")
21679      * @type String
21680      */
21681     beforePageText : "Page",
21682     /**
21683      * Customizable piece of the default paging text (defaults to "of %0")
21684      * @type String
21685      */
21686     afterPageText : "of {0}",
21687     /**
21688      * Customizable piece of the default paging text (defaults to "First Page")
21689      * @type String
21690      */
21691     firstText : "First Page",
21692     /**
21693      * Customizable piece of the default paging text (defaults to "Previous Page")
21694      * @type String
21695      */
21696     prevText : "Previous Page",
21697     /**
21698      * Customizable piece of the default paging text (defaults to "Next Page")
21699      * @type String
21700      */
21701     nextText : "Next Page",
21702     /**
21703      * Customizable piece of the default paging text (defaults to "Last Page")
21704      * @type String
21705      */
21706     lastText : "Last Page",
21707     /**
21708      * Customizable piece of the default paging text (defaults to "Refresh")
21709      * @type String
21710      */
21711     refreshText : "Refresh",
21712
21713     buttons : false,
21714     // private
21715     onRender : function(ct, position) 
21716     {
21717         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21718         this.navgroup.parentId = this.id;
21719         this.navgroup.onRender(this.el, null);
21720         // add the buttons to the navgroup
21721         
21722         if(this.displayInfo){
21723             Roo.log(this.el.select('ul.navbar-nav',true).first());
21724             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21725             this.displayEl = this.el.select('.x-paging-info', true).first();
21726 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21727 //            this.displayEl = navel.el.select('span',true).first();
21728         }
21729         
21730         var _this = this;
21731         
21732         if(this.buttons){
21733             Roo.each(_this.buttons, function(e){
21734                Roo.factory(e).onRender(_this.el, null);
21735             });
21736         }
21737             
21738         Roo.each(_this.toolbarItems, function(e) {
21739             _this.navgroup.addItem(e);
21740         });
21741         
21742         
21743         this.first = this.navgroup.addItem({
21744             tooltip: this.firstText,
21745             cls: "prev",
21746             icon : 'fa fa-backward',
21747             disabled: true,
21748             preventDefault: true,
21749             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21750         });
21751         
21752         this.prev =  this.navgroup.addItem({
21753             tooltip: this.prevText,
21754             cls: "prev",
21755             icon : 'fa fa-step-backward',
21756             disabled: true,
21757             preventDefault: true,
21758             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21759         });
21760     //this.addSeparator();
21761         
21762         
21763         var field = this.navgroup.addItem( {
21764             tagtype : 'span',
21765             cls : 'x-paging-position',
21766             
21767             html : this.beforePageText  +
21768                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21769                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21770          } ); //?? escaped?
21771         
21772         this.field = field.el.select('input', true).first();
21773         this.field.on("keydown", this.onPagingKeydown, this);
21774         this.field.on("focus", function(){this.dom.select();});
21775     
21776     
21777         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21778         //this.field.setHeight(18);
21779         //this.addSeparator();
21780         this.next = this.navgroup.addItem({
21781             tooltip: this.nextText,
21782             cls: "next",
21783             html : ' <i class="fa fa-step-forward">',
21784             disabled: true,
21785             preventDefault: true,
21786             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21787         });
21788         this.last = this.navgroup.addItem({
21789             tooltip: this.lastText,
21790             icon : 'fa fa-forward',
21791             cls: "next",
21792             disabled: true,
21793             preventDefault: true,
21794             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21795         });
21796     //this.addSeparator();
21797         this.loading = this.navgroup.addItem({
21798             tooltip: this.refreshText,
21799             icon: 'fa fa-refresh',
21800             preventDefault: true,
21801             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21802         });
21803
21804     },
21805
21806     // private
21807     updateInfo : function(){
21808         if(this.displayEl){
21809             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21810             var msg = count == 0 ?
21811                 this.emptyMsg :
21812                 String.format(
21813                     this.displayMsg,
21814                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21815                 );
21816             this.displayEl.update(msg);
21817         }
21818     },
21819
21820     // private
21821     onLoad : function(ds, r, o){
21822        this.cursor = o.params ? o.params.start : 0;
21823        var d = this.getPageData(),
21824             ap = d.activePage,
21825             ps = d.pages;
21826         
21827        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21828        this.field.dom.value = ap;
21829        this.first.setDisabled(ap == 1);
21830        this.prev.setDisabled(ap == 1);
21831        this.next.setDisabled(ap == ps);
21832        this.last.setDisabled(ap == ps);
21833        this.loading.enable();
21834        this.updateInfo();
21835     },
21836
21837     // private
21838     getPageData : function(){
21839         var total = this.ds.getTotalCount();
21840         return {
21841             total : total,
21842             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21843             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21844         };
21845     },
21846
21847     // private
21848     onLoadError : function(){
21849         this.loading.enable();
21850     },
21851
21852     // private
21853     onPagingKeydown : function(e){
21854         var k = e.getKey();
21855         var d = this.getPageData();
21856         if(k == e.RETURN){
21857             var v = this.field.dom.value, pageNum;
21858             if(!v || isNaN(pageNum = parseInt(v, 10))){
21859                 this.field.dom.value = d.activePage;
21860                 return;
21861             }
21862             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21863             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21864             e.stopEvent();
21865         }
21866         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))
21867         {
21868           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21869           this.field.dom.value = pageNum;
21870           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21871           e.stopEvent();
21872         }
21873         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21874         {
21875           var v = this.field.dom.value, pageNum; 
21876           var increment = (e.shiftKey) ? 10 : 1;
21877           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21878             increment *= -1;
21879           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21880             this.field.dom.value = d.activePage;
21881             return;
21882           }
21883           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21884           {
21885             this.field.dom.value = parseInt(v, 10) + increment;
21886             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21887             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21888           }
21889           e.stopEvent();
21890         }
21891     },
21892
21893     // private
21894     beforeLoad : function(){
21895         if(this.loading){
21896             this.loading.disable();
21897         }
21898     },
21899
21900     // private
21901     onClick : function(which){
21902         
21903         var ds = this.ds;
21904         if (!ds) {
21905             return;
21906         }
21907         
21908         switch(which){
21909             case "first":
21910                 ds.load({params:{start: 0, limit: this.pageSize}});
21911             break;
21912             case "prev":
21913                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21914             break;
21915             case "next":
21916                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21917             break;
21918             case "last":
21919                 var total = ds.getTotalCount();
21920                 var extra = total % this.pageSize;
21921                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21922                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21923             break;
21924             case "refresh":
21925                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21926             break;
21927         }
21928     },
21929
21930     /**
21931      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21932      * @param {Roo.data.Store} store The data store to unbind
21933      */
21934     unbind : function(ds){
21935         ds.un("beforeload", this.beforeLoad, this);
21936         ds.un("load", this.onLoad, this);
21937         ds.un("loadexception", this.onLoadError, this);
21938         ds.un("remove", this.updateInfo, this);
21939         ds.un("add", this.updateInfo, this);
21940         this.ds = undefined;
21941     },
21942
21943     /**
21944      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21945      * @param {Roo.data.Store} store The data store to bind
21946      */
21947     bind : function(ds){
21948         ds.on("beforeload", this.beforeLoad, this);
21949         ds.on("load", this.onLoad, this);
21950         ds.on("loadexception", this.onLoadError, this);
21951         ds.on("remove", this.updateInfo, this);
21952         ds.on("add", this.updateInfo, this);
21953         this.ds = ds;
21954     }
21955 });/*
21956  * - LGPL
21957  *
21958  * element
21959  * 
21960  */
21961
21962 /**
21963  * @class Roo.bootstrap.MessageBar
21964  * @extends Roo.bootstrap.Component
21965  * Bootstrap MessageBar class
21966  * @cfg {String} html contents of the MessageBar
21967  * @cfg {String} weight (info | success | warning | danger) default info
21968  * @cfg {String} beforeClass insert the bar before the given class
21969  * @cfg {Boolean} closable (true | false) default false
21970  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21971  * 
21972  * @constructor
21973  * Create a new Element
21974  * @param {Object} config The config object
21975  */
21976
21977 Roo.bootstrap.MessageBar = function(config){
21978     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21979 };
21980
21981 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21982     
21983     html: '',
21984     weight: 'info',
21985     closable: false,
21986     fixed: false,
21987     beforeClass: 'bootstrap-sticky-wrap',
21988     
21989     getAutoCreate : function(){
21990         
21991         var cfg = {
21992             tag: 'div',
21993             cls: 'alert alert-dismissable alert-' + this.weight,
21994             cn: [
21995                 {
21996                     tag: 'span',
21997                     cls: 'message',
21998                     html: this.html || ''
21999                 }
22000             ]
22001         }
22002         
22003         if(this.fixed){
22004             cfg.cls += ' alert-messages-fixed';
22005         }
22006         
22007         if(this.closable){
22008             cfg.cn.push({
22009                 tag: 'button',
22010                 cls: 'close',
22011                 html: 'x'
22012             });
22013         }
22014         
22015         return cfg;
22016     },
22017     
22018     onRender : function(ct, position)
22019     {
22020         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22021         
22022         if(!this.el){
22023             var cfg = Roo.apply({},  this.getAutoCreate());
22024             cfg.id = Roo.id();
22025             
22026             if (this.cls) {
22027                 cfg.cls += ' ' + this.cls;
22028             }
22029             if (this.style) {
22030                 cfg.style = this.style;
22031             }
22032             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22033             
22034             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22035         }
22036         
22037         this.el.select('>button.close').on('click', this.hide, this);
22038         
22039     },
22040     
22041     show : function()
22042     {
22043         if (!this.rendered) {
22044             this.render();
22045         }
22046         
22047         this.el.show();
22048         
22049         this.fireEvent('show', this);
22050         
22051     },
22052     
22053     hide : function()
22054     {
22055         if (!this.rendered) {
22056             this.render();
22057         }
22058         
22059         this.el.hide();
22060         
22061         this.fireEvent('hide', this);
22062     },
22063     
22064     update : function()
22065     {
22066 //        var e = this.el.dom.firstChild;
22067 //        
22068 //        if(this.closable){
22069 //            e = e.nextSibling;
22070 //        }
22071 //        
22072 //        e.data = this.html || '';
22073
22074         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22075     }
22076    
22077 });
22078
22079  
22080
22081      /*
22082  * - LGPL
22083  *
22084  * Graph
22085  * 
22086  */
22087
22088
22089 /**
22090  * @class Roo.bootstrap.Graph
22091  * @extends Roo.bootstrap.Component
22092  * Bootstrap Graph class
22093 > Prameters
22094  -sm {number} sm 4
22095  -md {number} md 5
22096  @cfg {String} graphtype  bar | vbar | pie
22097  @cfg {number} g_x coodinator | centre x (pie)
22098  @cfg {number} g_y coodinator | centre y (pie)
22099  @cfg {number} g_r radius (pie)
22100  @cfg {number} g_height height of the chart (respected by all elements in the set)
22101  @cfg {number} g_width width of the chart (respected by all elements in the set)
22102  @cfg {Object} title The title of the chart
22103     
22104  -{Array}  values
22105  -opts (object) options for the chart 
22106      o {
22107      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22108      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22109      o vgutter (number)
22110      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.
22111      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22112      o to
22113      o stretch (boolean)
22114      o }
22115  -opts (object) options for the pie
22116      o{
22117      o cut
22118      o startAngle (number)
22119      o endAngle (number)
22120      } 
22121  *
22122  * @constructor
22123  * Create a new Input
22124  * @param {Object} config The config object
22125  */
22126
22127 Roo.bootstrap.Graph = function(config){
22128     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22129     
22130     this.addEvents({
22131         // img events
22132         /**
22133          * @event click
22134          * The img click event for the img.
22135          * @param {Roo.EventObject} e
22136          */
22137         "click" : true
22138     });
22139 };
22140
22141 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22142     
22143     sm: 4,
22144     md: 5,
22145     graphtype: 'bar',
22146     g_height: 250,
22147     g_width: 400,
22148     g_x: 50,
22149     g_y: 50,
22150     g_r: 30,
22151     opts:{
22152         //g_colors: this.colors,
22153         g_type: 'soft',
22154         g_gutter: '20%'
22155
22156     },
22157     title : false,
22158
22159     getAutoCreate : function(){
22160         
22161         var cfg = {
22162             tag: 'div',
22163             html : null
22164         }
22165         
22166         
22167         return  cfg;
22168     },
22169
22170     onRender : function(ct,position){
22171         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22172         this.raphael = Raphael(this.el.dom);
22173         
22174                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22175                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22176                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22177                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22178                 /*
22179                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22180                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22181                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22182                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22183                 
22184                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22185                 r.barchart(330, 10, 300, 220, data1);
22186                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22187                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22188                 */
22189                 
22190                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22191                 // r.barchart(30, 30, 560, 250,  xdata, {
22192                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22193                 //     axis : "0 0 1 1",
22194                 //     axisxlabels :  xdata
22195                 //     //yvalues : cols,
22196                    
22197                 // });
22198 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22199 //        
22200 //        this.load(null,xdata,{
22201 //                axis : "0 0 1 1",
22202 //                axisxlabels :  xdata
22203 //                });
22204
22205     },
22206
22207     load : function(graphtype,xdata,opts){
22208         this.raphael.clear();
22209         if(!graphtype) {
22210             graphtype = this.graphtype;
22211         }
22212         if(!opts){
22213             opts = this.opts;
22214         }
22215         var r = this.raphael,
22216             fin = function () {
22217                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22218             },
22219             fout = function () {
22220                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22221             },
22222             pfin = function() {
22223                 this.sector.stop();
22224                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22225
22226                 if (this.label) {
22227                     this.label[0].stop();
22228                     this.label[0].attr({ r: 7.5 });
22229                     this.label[1].attr({ "font-weight": 800 });
22230                 }
22231             },
22232             pfout = function() {
22233                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22234
22235                 if (this.label) {
22236                     this.label[0].animate({ r: 5 }, 500, "bounce");
22237                     this.label[1].attr({ "font-weight": 400 });
22238                 }
22239             };
22240
22241         switch(graphtype){
22242             case 'bar':
22243                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22244                 break;
22245             case 'hbar':
22246                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22247                 break;
22248             case 'pie':
22249 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22250 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22251 //            
22252                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22253                 
22254                 break;
22255
22256         }
22257         
22258         if(this.title){
22259             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22260         }
22261         
22262     },
22263     
22264     setTitle: function(o)
22265     {
22266         this.title = o;
22267     },
22268     
22269     initEvents: function() {
22270         
22271         if(!this.href){
22272             this.el.on('click', this.onClick, this);
22273         }
22274     },
22275     
22276     onClick : function(e)
22277     {
22278         Roo.log('img onclick');
22279         this.fireEvent('click', this, e);
22280     }
22281    
22282 });
22283
22284  
22285 /*
22286  * - LGPL
22287  *
22288  * numberBox
22289  * 
22290  */
22291 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22292
22293 /**
22294  * @class Roo.bootstrap.dash.NumberBox
22295  * @extends Roo.bootstrap.Component
22296  * Bootstrap NumberBox class
22297  * @cfg {String} headline Box headline
22298  * @cfg {String} content Box content
22299  * @cfg {String} icon Box icon
22300  * @cfg {String} footer Footer text
22301  * @cfg {String} fhref Footer href
22302  * 
22303  * @constructor
22304  * Create a new NumberBox
22305  * @param {Object} config The config object
22306  */
22307
22308
22309 Roo.bootstrap.dash.NumberBox = function(config){
22310     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22311     
22312 };
22313
22314 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22315     
22316     headline : '',
22317     content : '',
22318     icon : '',
22319     footer : '',
22320     fhref : '',
22321     ficon : '',
22322     
22323     getAutoCreate : function(){
22324         
22325         var cfg = {
22326             tag : 'div',
22327             cls : 'small-box ',
22328             cn : [
22329                 {
22330                     tag : 'div',
22331                     cls : 'inner',
22332                     cn :[
22333                         {
22334                             tag : 'h3',
22335                             cls : 'roo-headline',
22336                             html : this.headline
22337                         },
22338                         {
22339                             tag : 'p',
22340                             cls : 'roo-content',
22341                             html : this.content
22342                         }
22343                     ]
22344                 }
22345             ]
22346         }
22347         
22348         if(this.icon){
22349             cfg.cn.push({
22350                 tag : 'div',
22351                 cls : 'icon',
22352                 cn :[
22353                     {
22354                         tag : 'i',
22355                         cls : 'ion ' + this.icon
22356                     }
22357                 ]
22358             });
22359         }
22360         
22361         if(this.footer){
22362             var footer = {
22363                 tag : 'a',
22364                 cls : 'small-box-footer',
22365                 href : this.fhref || '#',
22366                 html : this.footer
22367             };
22368             
22369             cfg.cn.push(footer);
22370             
22371         }
22372         
22373         return  cfg;
22374     },
22375
22376     onRender : function(ct,position){
22377         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22378
22379
22380        
22381                 
22382     },
22383
22384     setHeadline: function (value)
22385     {
22386         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22387     },
22388     
22389     setFooter: function (value, href)
22390     {
22391         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22392         
22393         if(href){
22394             this.el.select('a.small-box-footer',true).first().attr('href', href);
22395         }
22396         
22397     },
22398
22399     setContent: function (value)
22400     {
22401         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22402     },
22403
22404     initEvents: function() 
22405     {   
22406         
22407     }
22408     
22409 });
22410
22411  
22412 /*
22413  * - LGPL
22414  *
22415  * TabBox
22416  * 
22417  */
22418 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22419
22420 /**
22421  * @class Roo.bootstrap.dash.TabBox
22422  * @extends Roo.bootstrap.Component
22423  * Bootstrap TabBox class
22424  * @cfg {String} title Title of the TabBox
22425  * @cfg {String} icon Icon of the TabBox
22426  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22427  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22428  * 
22429  * @constructor
22430  * Create a new TabBox
22431  * @param {Object} config The config object
22432  */
22433
22434
22435 Roo.bootstrap.dash.TabBox = function(config){
22436     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22437     this.addEvents({
22438         // raw events
22439         /**
22440          * @event addpane
22441          * When a pane is added
22442          * @param {Roo.bootstrap.dash.TabPane} pane
22443          */
22444         "addpane" : true,
22445         /**
22446          * @event activatepane
22447          * When a pane is activated
22448          * @param {Roo.bootstrap.dash.TabPane} pane
22449          */
22450         "activatepane" : true
22451         
22452          
22453     });
22454     
22455     this.panes = [];
22456 };
22457
22458 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22459
22460     title : '',
22461     icon : false,
22462     showtabs : true,
22463     tabScrollable : false,
22464     
22465     getChildContainer : function()
22466     {
22467         return this.el.select('.tab-content', true).first();
22468     },
22469     
22470     getAutoCreate : function(){
22471         
22472         var header = {
22473             tag: 'li',
22474             cls: 'pull-left header',
22475             html: this.title,
22476             cn : []
22477         };
22478         
22479         if(this.icon){
22480             header.cn.push({
22481                 tag: 'i',
22482                 cls: 'fa ' + this.icon
22483             });
22484         }
22485         
22486         var h = {
22487             tag: 'ul',
22488             cls: 'nav nav-tabs pull-right',
22489             cn: [
22490                 header
22491             ]
22492         };
22493         
22494         if(this.tabScrollable){
22495             h = {
22496                 tag: 'div',
22497                 cls: 'tab-header',
22498                 cn: [
22499                     {
22500                         tag: 'ul',
22501                         cls: 'nav nav-tabs pull-right',
22502                         cn: [
22503                             header
22504                         ]
22505                     }
22506                 ]
22507             }
22508         }
22509         
22510         var cfg = {
22511             tag: 'div',
22512             cls: 'nav-tabs-custom',
22513             cn: [
22514                 h,
22515                 {
22516                     tag: 'div',
22517                     cls: 'tab-content no-padding',
22518                     cn: []
22519                 }
22520             ]
22521         }
22522
22523         return  cfg;
22524     },
22525     initEvents : function()
22526     {
22527         //Roo.log('add add pane handler');
22528         this.on('addpane', this.onAddPane, this);
22529     },
22530      /**
22531      * Updates the box title
22532      * @param {String} html to set the title to.
22533      */
22534     setTitle : function(value)
22535     {
22536         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22537     },
22538     onAddPane : function(pane)
22539     {
22540         this.panes.push(pane);
22541         //Roo.log('addpane');
22542         //Roo.log(pane);
22543         // tabs are rendere left to right..
22544         if(!this.showtabs){
22545             return;
22546         }
22547         
22548         var ctr = this.el.select('.nav-tabs', true).first();
22549          
22550          
22551         var existing = ctr.select('.nav-tab',true);
22552         var qty = existing.getCount();;
22553         
22554         
22555         var tab = ctr.createChild({
22556             tag : 'li',
22557             cls : 'nav-tab' + (qty ? '' : ' active'),
22558             cn : [
22559                 {
22560                     tag : 'a',
22561                     href:'#',
22562                     html : pane.title
22563                 }
22564             ]
22565         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22566         pane.tab = tab;
22567         
22568         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22569         if (!qty) {
22570             pane.el.addClass('active');
22571         }
22572         
22573                 
22574     },
22575     onTabClick : function(ev,un,ob,pane)
22576     {
22577         //Roo.log('tab - prev default');
22578         ev.preventDefault();
22579         
22580         
22581         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22582         pane.tab.addClass('active');
22583         //Roo.log(pane.title);
22584         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22585         // technically we should have a deactivate event.. but maybe add later.
22586         // and it should not de-activate the selected tab...
22587         this.fireEvent('activatepane', pane);
22588         pane.el.addClass('active');
22589         pane.fireEvent('activate');
22590         
22591         
22592     },
22593     
22594     getActivePane : function()
22595     {
22596         var r = false;
22597         Roo.each(this.panes, function(p) {
22598             if(p.el.hasClass('active')){
22599                 r = p;
22600                 return false;
22601             }
22602             
22603             return;
22604         });
22605         
22606         return r;
22607     }
22608     
22609     
22610 });
22611
22612  
22613 /*
22614  * - LGPL
22615  *
22616  * Tab pane
22617  * 
22618  */
22619 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22620 /**
22621  * @class Roo.bootstrap.TabPane
22622  * @extends Roo.bootstrap.Component
22623  * Bootstrap TabPane class
22624  * @cfg {Boolean} active (false | true) Default false
22625  * @cfg {String} title title of panel
22626
22627  * 
22628  * @constructor
22629  * Create a new TabPane
22630  * @param {Object} config The config object
22631  */
22632
22633 Roo.bootstrap.dash.TabPane = function(config){
22634     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22635     
22636     this.addEvents({
22637         // raw events
22638         /**
22639          * @event activate
22640          * When a pane is activated
22641          * @param {Roo.bootstrap.dash.TabPane} pane
22642          */
22643         "activate" : true
22644          
22645     });
22646 };
22647
22648 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22649     
22650     active : false,
22651     title : '',
22652     
22653     // the tabBox that this is attached to.
22654     tab : false,
22655      
22656     getAutoCreate : function() 
22657     {
22658         var cfg = {
22659             tag: 'div',
22660             cls: 'tab-pane'
22661         }
22662         
22663         if(this.active){
22664             cfg.cls += ' active';
22665         }
22666         
22667         return cfg;
22668     },
22669     initEvents  : function()
22670     {
22671         //Roo.log('trigger add pane handler');
22672         this.parent().fireEvent('addpane', this)
22673     },
22674     
22675      /**
22676      * Updates the tab title 
22677      * @param {String} html to set the title to.
22678      */
22679     setTitle: function(str)
22680     {
22681         if (!this.tab) {
22682             return;
22683         }
22684         this.title = str;
22685         this.tab.select('a', true).first().dom.innerHTML = str;
22686         
22687     }
22688     
22689     
22690     
22691 });
22692
22693  
22694
22695
22696  /*
22697  * - LGPL
22698  *
22699  * menu
22700  * 
22701  */
22702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22703
22704 /**
22705  * @class Roo.bootstrap.menu.Menu
22706  * @extends Roo.bootstrap.Component
22707  * Bootstrap Menu class - container for Menu
22708  * @cfg {String} html Text of the menu
22709  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22710  * @cfg {String} icon Font awesome icon
22711  * @cfg {String} pos Menu align to (top | bottom) default bottom
22712  * 
22713  * 
22714  * @constructor
22715  * Create a new Menu
22716  * @param {Object} config The config object
22717  */
22718
22719
22720 Roo.bootstrap.menu.Menu = function(config){
22721     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22722     
22723     this.addEvents({
22724         /**
22725          * @event beforeshow
22726          * Fires before this menu is displayed
22727          * @param {Roo.bootstrap.menu.Menu} this
22728          */
22729         beforeshow : true,
22730         /**
22731          * @event beforehide
22732          * Fires before this menu is hidden
22733          * @param {Roo.bootstrap.menu.Menu} this
22734          */
22735         beforehide : true,
22736         /**
22737          * @event show
22738          * Fires after this menu is displayed
22739          * @param {Roo.bootstrap.menu.Menu} this
22740          */
22741         show : true,
22742         /**
22743          * @event hide
22744          * Fires after this menu is hidden
22745          * @param {Roo.bootstrap.menu.Menu} this
22746          */
22747         hide : true,
22748         /**
22749          * @event click
22750          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22751          * @param {Roo.bootstrap.menu.Menu} this
22752          * @param {Roo.EventObject} e
22753          */
22754         click : true
22755     });
22756     
22757 };
22758
22759 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22760     
22761     submenu : false,
22762     html : '',
22763     weight : 'default',
22764     icon : false,
22765     pos : 'bottom',
22766     
22767     
22768     getChildContainer : function() {
22769         if(this.isSubMenu){
22770             return this.el;
22771         }
22772         
22773         return this.el.select('ul.dropdown-menu', true).first();  
22774     },
22775     
22776     getAutoCreate : function()
22777     {
22778         var text = [
22779             {
22780                 tag : 'span',
22781                 cls : 'roo-menu-text',
22782                 html : this.html
22783             }
22784         ];
22785         
22786         if(this.icon){
22787             text.unshift({
22788                 tag : 'i',
22789                 cls : 'fa ' + this.icon
22790             })
22791         }
22792         
22793         
22794         var cfg = {
22795             tag : 'div',
22796             cls : 'btn-group',
22797             cn : [
22798                 {
22799                     tag : 'button',
22800                     cls : 'dropdown-button btn btn-' + this.weight,
22801                     cn : text
22802                 },
22803                 {
22804                     tag : 'button',
22805                     cls : 'dropdown-toggle btn btn-' + this.weight,
22806                     cn : [
22807                         {
22808                             tag : 'span',
22809                             cls : 'caret'
22810                         }
22811                     ]
22812                 },
22813                 {
22814                     tag : 'ul',
22815                     cls : 'dropdown-menu'
22816                 }
22817             ]
22818             
22819         };
22820         
22821         if(this.pos == 'top'){
22822             cfg.cls += ' dropup';
22823         }
22824         
22825         if(this.isSubMenu){
22826             cfg = {
22827                 tag : 'ul',
22828                 cls : 'dropdown-menu'
22829             }
22830         }
22831         
22832         return cfg;
22833     },
22834     
22835     onRender : function(ct, position)
22836     {
22837         this.isSubMenu = ct.hasClass('dropdown-submenu');
22838         
22839         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22840     },
22841     
22842     initEvents : function() 
22843     {
22844         if(this.isSubMenu){
22845             return;
22846         }
22847         
22848         this.hidden = true;
22849         
22850         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22851         this.triggerEl.on('click', this.onTriggerPress, this);
22852         
22853         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22854         this.buttonEl.on('click', this.onClick, this);
22855         
22856     },
22857     
22858     list : function()
22859     {
22860         if(this.isSubMenu){
22861             return this.el;
22862         }
22863         
22864         return this.el.select('ul.dropdown-menu', true).first();
22865     },
22866     
22867     onClick : function(e)
22868     {
22869         this.fireEvent("click", this, e);
22870     },
22871     
22872     onTriggerPress  : function(e)
22873     {   
22874         if (this.isVisible()) {
22875             this.hide();
22876         } else {
22877             this.show();
22878         }
22879     },
22880     
22881     isVisible : function(){
22882         return !this.hidden;
22883     },
22884     
22885     show : function()
22886     {
22887         this.fireEvent("beforeshow", this);
22888         
22889         this.hidden = false;
22890         this.el.addClass('open');
22891         
22892         Roo.get(document).on("mouseup", this.onMouseUp, this);
22893         
22894         this.fireEvent("show", this);
22895         
22896         
22897     },
22898     
22899     hide : function()
22900     {
22901         this.fireEvent("beforehide", this);
22902         
22903         this.hidden = true;
22904         this.el.removeClass('open');
22905         
22906         Roo.get(document).un("mouseup", this.onMouseUp);
22907         
22908         this.fireEvent("hide", this);
22909     },
22910     
22911     onMouseUp : function()
22912     {
22913         this.hide();
22914     }
22915     
22916 });
22917
22918  
22919  /*
22920  * - LGPL
22921  *
22922  * menu item
22923  * 
22924  */
22925 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22926
22927 /**
22928  * @class Roo.bootstrap.menu.Item
22929  * @extends Roo.bootstrap.Component
22930  * Bootstrap MenuItem class
22931  * @cfg {Boolean} submenu (true | false) default false
22932  * @cfg {String} html text of the item
22933  * @cfg {String} href the link
22934  * @cfg {Boolean} disable (true | false) default false
22935  * @cfg {Boolean} preventDefault (true | false) default true
22936  * @cfg {String} icon Font awesome icon
22937  * @cfg {String} pos Submenu align to (left | right) default right 
22938  * 
22939  * 
22940  * @constructor
22941  * Create a new Item
22942  * @param {Object} config The config object
22943  */
22944
22945
22946 Roo.bootstrap.menu.Item = function(config){
22947     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22948     this.addEvents({
22949         /**
22950          * @event mouseover
22951          * Fires when the mouse is hovering over this menu
22952          * @param {Roo.bootstrap.menu.Item} this
22953          * @param {Roo.EventObject} e
22954          */
22955         mouseover : true,
22956         /**
22957          * @event mouseout
22958          * Fires when the mouse exits this menu
22959          * @param {Roo.bootstrap.menu.Item} this
22960          * @param {Roo.EventObject} e
22961          */
22962         mouseout : true,
22963         // raw events
22964         /**
22965          * @event click
22966          * The raw click event for the entire grid.
22967          * @param {Roo.EventObject} e
22968          */
22969         click : true
22970     });
22971 };
22972
22973 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22974     
22975     submenu : false,
22976     href : '',
22977     html : '',
22978     preventDefault: true,
22979     disable : false,
22980     icon : false,
22981     pos : 'right',
22982     
22983     getAutoCreate : function()
22984     {
22985         var text = [
22986             {
22987                 tag : 'span',
22988                 cls : 'roo-menu-item-text',
22989                 html : this.html
22990             }
22991         ];
22992         
22993         if(this.icon){
22994             text.unshift({
22995                 tag : 'i',
22996                 cls : 'fa ' + this.icon
22997             })
22998         }
22999         
23000         var cfg = {
23001             tag : 'li',
23002             cn : [
23003                 {
23004                     tag : 'a',
23005                     href : this.href || '#',
23006                     cn : text
23007                 }
23008             ]
23009         };
23010         
23011         if(this.disable){
23012             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23013         }
23014         
23015         if(this.submenu){
23016             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23017             
23018             if(this.pos == 'left'){
23019                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23020             }
23021         }
23022         
23023         return cfg;
23024     },
23025     
23026     initEvents : function() 
23027     {
23028         this.el.on('mouseover', this.onMouseOver, this);
23029         this.el.on('mouseout', this.onMouseOut, this);
23030         
23031         this.el.select('a', true).first().on('click', this.onClick, this);
23032         
23033     },
23034     
23035     onClick : function(e)
23036     {
23037         if(this.preventDefault){
23038             e.preventDefault();
23039         }
23040         
23041         this.fireEvent("click", this, e);
23042     },
23043     
23044     onMouseOver : function(e)
23045     {
23046         if(this.submenu && this.pos == 'left'){
23047             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23048         }
23049         
23050         this.fireEvent("mouseover", this, e);
23051     },
23052     
23053     onMouseOut : function(e)
23054     {
23055         this.fireEvent("mouseout", this, e);
23056     }
23057 });
23058
23059  
23060
23061  /*
23062  * - LGPL
23063  *
23064  * menu separator
23065  * 
23066  */
23067 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23068
23069 /**
23070  * @class Roo.bootstrap.menu.Separator
23071  * @extends Roo.bootstrap.Component
23072  * Bootstrap Separator class
23073  * 
23074  * @constructor
23075  * Create a new Separator
23076  * @param {Object} config The config object
23077  */
23078
23079
23080 Roo.bootstrap.menu.Separator = function(config){
23081     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23082 };
23083
23084 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23085     
23086     getAutoCreate : function(){
23087         var cfg = {
23088             tag : 'li',
23089             cls: 'divider'
23090         };
23091         
23092         return cfg;
23093     }
23094    
23095 });
23096
23097  
23098
23099  /*
23100  * - LGPL
23101  *
23102  * Tooltip
23103  * 
23104  */
23105
23106 /**
23107  * @class Roo.bootstrap.Tooltip
23108  * Bootstrap Tooltip class
23109  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23110  * to determine which dom element triggers the tooltip.
23111  * 
23112  * It needs to add support for additional attributes like tooltip-position
23113  * 
23114  * @constructor
23115  * Create a new Toolti
23116  * @param {Object} config The config object
23117  */
23118
23119 Roo.bootstrap.Tooltip = function(config){
23120     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23121 };
23122
23123 Roo.apply(Roo.bootstrap.Tooltip, {
23124     /**
23125      * @function init initialize tooltip monitoring.
23126      * @static
23127      */
23128     currentEl : false,
23129     currentTip : false,
23130     currentRegion : false,
23131     
23132     //  init : delay?
23133     
23134     init : function()
23135     {
23136         Roo.get(document).on('mouseover', this.enter ,this);
23137         Roo.get(document).on('mouseout', this.leave, this);
23138          
23139         
23140         this.currentTip = new Roo.bootstrap.Tooltip();
23141     },
23142     
23143     enter : function(ev)
23144     {
23145         var dom = ev.getTarget();
23146         
23147         //Roo.log(['enter',dom]);
23148         var el = Roo.fly(dom);
23149         if (this.currentEl) {
23150             //Roo.log(dom);
23151             //Roo.log(this.currentEl);
23152             //Roo.log(this.currentEl.contains(dom));
23153             if (this.currentEl == el) {
23154                 return;
23155             }
23156             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23157                 return;
23158             }
23159
23160         }
23161         
23162         
23163         
23164         if (this.currentTip.el) {
23165             this.currentTip.el.hide(); // force hiding...
23166         }    
23167         //Roo.log(ev);
23168         var bindEl = el;
23169         
23170         // you can not look for children, as if el is the body.. then everythign is the child..
23171         if (!el.attr('tooltip')) { //
23172             if (!el.select("[tooltip]").elements.length) {
23173                 return;
23174             }
23175             // is the mouse over this child...?
23176             bindEl = el.select("[tooltip]").first();
23177             var xy = ev.getXY();
23178             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23179                 //Roo.log("not in region.");
23180                 return;
23181             }
23182             //Roo.log("child element over..");
23183             
23184         }
23185         this.currentEl = bindEl;
23186         this.currentTip.bind(bindEl);
23187         this.currentRegion = Roo.lib.Region.getRegion(dom);
23188         this.currentTip.enter();
23189         
23190     },
23191     leave : function(ev)
23192     {
23193         var dom = ev.getTarget();
23194         //Roo.log(['leave',dom]);
23195         if (!this.currentEl) {
23196             return;
23197         }
23198         
23199         
23200         if (dom != this.currentEl.dom) {
23201             return;
23202         }
23203         var xy = ev.getXY();
23204         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23205             return;
23206         }
23207         // only activate leave if mouse cursor is outside... bounding box..
23208         
23209         
23210         
23211         
23212         if (this.currentTip) {
23213             this.currentTip.leave();
23214         }
23215         //Roo.log('clear currentEl');
23216         this.currentEl = false;
23217         
23218         
23219     },
23220     alignment : {
23221         'left' : ['r-l', [-2,0], 'right'],
23222         'right' : ['l-r', [2,0], 'left'],
23223         'bottom' : ['t-b', [0,2], 'top'],
23224         'top' : [ 'b-t', [0,-2], 'bottom']
23225     }
23226     
23227 });
23228
23229
23230 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23231     
23232     
23233     bindEl : false,
23234     
23235     delay : null, // can be { show : 300 , hide: 500}
23236     
23237     timeout : null,
23238     
23239     hoverState : null, //???
23240     
23241     placement : 'bottom', 
23242     
23243     getAutoCreate : function(){
23244     
23245         var cfg = {
23246            cls : 'tooltip',
23247            role : 'tooltip',
23248            cn : [
23249                 {
23250                     cls : 'tooltip-arrow'
23251                 },
23252                 {
23253                     cls : 'tooltip-inner'
23254                 }
23255            ]
23256         };
23257         
23258         return cfg;
23259     },
23260     bind : function(el)
23261     {
23262         this.bindEl = el;
23263     },
23264       
23265     
23266     enter : function () {
23267        
23268         if (this.timeout != null) {
23269             clearTimeout(this.timeout);
23270         }
23271         
23272         this.hoverState = 'in';
23273          //Roo.log("enter - show");
23274         if (!this.delay || !this.delay.show) {
23275             this.show();
23276             return;
23277         }
23278         var _t = this;
23279         this.timeout = setTimeout(function () {
23280             if (_t.hoverState == 'in') {
23281                 _t.show();
23282             }
23283         }, this.delay.show);
23284     },
23285     leave : function()
23286     {
23287         clearTimeout(this.timeout);
23288     
23289         this.hoverState = 'out';
23290          if (!this.delay || !this.delay.hide) {
23291             this.hide();
23292             return;
23293         }
23294        
23295         var _t = this;
23296         this.timeout = setTimeout(function () {
23297             //Roo.log("leave - timeout");
23298             
23299             if (_t.hoverState == 'out') {
23300                 _t.hide();
23301                 Roo.bootstrap.Tooltip.currentEl = false;
23302             }
23303         }, delay);
23304     },
23305     
23306     show : function ()
23307     {
23308         if (!this.el) {
23309             this.render(document.body);
23310         }
23311         // set content.
23312         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23313         
23314         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23315         
23316         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23317         
23318         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23319         
23320         var placement = typeof this.placement == 'function' ?
23321             this.placement.call(this, this.el, on_el) :
23322             this.placement;
23323             
23324         var autoToken = /\s?auto?\s?/i;
23325         var autoPlace = autoToken.test(placement);
23326         if (autoPlace) {
23327             placement = placement.replace(autoToken, '') || 'top';
23328         }
23329         
23330         //this.el.detach()
23331         //this.el.setXY([0,0]);
23332         this.el.show();
23333         //this.el.dom.style.display='block';
23334         this.el.addClass(placement);
23335         
23336         //this.el.appendTo(on_el);
23337         
23338         var p = this.getPosition();
23339         var box = this.el.getBox();
23340         
23341         if (autoPlace) {
23342             // fixme..
23343         }
23344         var align = Roo.bootstrap.Tooltip.alignment[placement];
23345         this.el.alignTo(this.bindEl, align[0],align[1]);
23346         //var arrow = this.el.select('.arrow',true).first();
23347         //arrow.set(align[2], 
23348         
23349         this.el.addClass('in fade');
23350         this.hoverState = null;
23351         
23352         if (this.el.hasClass('fade')) {
23353             // fade it?
23354         }
23355         
23356     },
23357     hide : function()
23358     {
23359          
23360         if (!this.el) {
23361             return;
23362         }
23363         //this.el.setXY([0,0]);
23364         this.el.removeClass('in');
23365         //this.el.hide();
23366         
23367     }
23368     
23369 });
23370  
23371
23372  /*
23373  * - LGPL
23374  *
23375  * Location Picker
23376  * 
23377  */
23378
23379 /**
23380  * @class Roo.bootstrap.LocationPicker
23381  * @extends Roo.bootstrap.Component
23382  * Bootstrap LocationPicker class
23383  * @cfg {Number} latitude Position when init default 0
23384  * @cfg {Number} longitude Position when init default 0
23385  * @cfg {Number} zoom default 15
23386  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23387  * @cfg {Boolean} mapTypeControl default false
23388  * @cfg {Boolean} disableDoubleClickZoom default false
23389  * @cfg {Boolean} scrollwheel default true
23390  * @cfg {Boolean} streetViewControl default false
23391  * @cfg {Number} radius default 0
23392  * @cfg {String} locationName
23393  * @cfg {Boolean} draggable default true
23394  * @cfg {Boolean} enableAutocomplete default false
23395  * @cfg {Boolean} enableReverseGeocode default true
23396  * @cfg {String} markerTitle
23397  * 
23398  * @constructor
23399  * Create a new LocationPicker
23400  * @param {Object} config The config object
23401  */
23402
23403
23404 Roo.bootstrap.LocationPicker = function(config){
23405     
23406     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23407     
23408     this.addEvents({
23409         /**
23410          * @event initial
23411          * Fires when the picker initialized.
23412          * @param {Roo.bootstrap.LocationPicker} this
23413          * @param {Google Location} location
23414          */
23415         initial : true,
23416         /**
23417          * @event positionchanged
23418          * Fires when the picker position changed.
23419          * @param {Roo.bootstrap.LocationPicker} this
23420          * @param {Google Location} location
23421          */
23422         positionchanged : true,
23423         /**
23424          * @event resize
23425          * Fires when the map resize.
23426          * @param {Roo.bootstrap.LocationPicker} this
23427          */
23428         resize : true,
23429         /**
23430          * @event show
23431          * Fires when the map show.
23432          * @param {Roo.bootstrap.LocationPicker} this
23433          */
23434         show : true,
23435         /**
23436          * @event hide
23437          * Fires when the map hide.
23438          * @param {Roo.bootstrap.LocationPicker} this
23439          */
23440         hide : true,
23441         /**
23442          * @event mapClick
23443          * Fires when click the map.
23444          * @param {Roo.bootstrap.LocationPicker} this
23445          * @param {Map event} e
23446          */
23447         mapClick : true,
23448         /**
23449          * @event mapRightClick
23450          * Fires when right click the map.
23451          * @param {Roo.bootstrap.LocationPicker} this
23452          * @param {Map event} e
23453          */
23454         mapRightClick : true,
23455         /**
23456          * @event markerClick
23457          * Fires when click the marker.
23458          * @param {Roo.bootstrap.LocationPicker} this
23459          * @param {Map event} e
23460          */
23461         markerClick : true,
23462         /**
23463          * @event markerRightClick
23464          * Fires when right click the marker.
23465          * @param {Roo.bootstrap.LocationPicker} this
23466          * @param {Map event} e
23467          */
23468         markerRightClick : true,
23469         /**
23470          * @event OverlayViewDraw
23471          * Fires when OverlayView Draw
23472          * @param {Roo.bootstrap.LocationPicker} this
23473          */
23474         OverlayViewDraw : true,
23475         /**
23476          * @event OverlayViewOnAdd
23477          * Fires when OverlayView Draw
23478          * @param {Roo.bootstrap.LocationPicker} this
23479          */
23480         OverlayViewOnAdd : true,
23481         /**
23482          * @event OverlayViewOnRemove
23483          * Fires when OverlayView Draw
23484          * @param {Roo.bootstrap.LocationPicker} this
23485          */
23486         OverlayViewOnRemove : true,
23487         /**
23488          * @event OverlayViewShow
23489          * Fires when OverlayView Draw
23490          * @param {Roo.bootstrap.LocationPicker} this
23491          * @param {Pixel} cpx
23492          */
23493         OverlayViewShow : true,
23494         /**
23495          * @event OverlayViewHide
23496          * Fires when OverlayView Draw
23497          * @param {Roo.bootstrap.LocationPicker} this
23498          */
23499         OverlayViewHide : true
23500     });
23501         
23502 };
23503
23504 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23505     
23506     gMapContext: false,
23507     
23508     latitude: 0,
23509     longitude: 0,
23510     zoom: 15,
23511     mapTypeId: false,
23512     mapTypeControl: false,
23513     disableDoubleClickZoom: false,
23514     scrollwheel: true,
23515     streetViewControl: false,
23516     radius: 0,
23517     locationName: '',
23518     draggable: true,
23519     enableAutocomplete: false,
23520     enableReverseGeocode: true,
23521     markerTitle: '',
23522     
23523     getAutoCreate: function()
23524     {
23525
23526         var cfg = {
23527             tag: 'div',
23528             cls: 'roo-location-picker'
23529         };
23530         
23531         return cfg
23532     },
23533     
23534     initEvents: function(ct, position)
23535     {       
23536         if(!this.el.getWidth() || this.isApplied()){
23537             return;
23538         }
23539         
23540         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23541         
23542         this.initial();
23543     },
23544     
23545     initial: function()
23546     {
23547         if(!this.mapTypeId){
23548             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23549         }
23550         
23551         this.gMapContext = this.GMapContext();
23552         
23553         this.initOverlayView();
23554         
23555         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23556         
23557         var _this = this;
23558                 
23559         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23560             _this.setPosition(_this.gMapContext.marker.position);
23561         });
23562         
23563         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23564             _this.fireEvent('mapClick', this, event);
23565             
23566         });
23567
23568         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23569             _this.fireEvent('mapRightClick', this, event);
23570             
23571         });
23572         
23573         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23574             _this.fireEvent('markerClick', this, event);
23575             
23576         });
23577
23578         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23579             _this.fireEvent('markerRightClick', this, event);
23580             
23581         });
23582         
23583         this.setPosition(this.gMapContext.location);
23584         
23585         this.fireEvent('initial', this, this.gMapContext.location);
23586     },
23587     
23588     initOverlayView: function()
23589     {
23590         var _this = this;
23591         
23592         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23593             
23594             draw: function()
23595             {
23596                 _this.fireEvent('OverlayViewDraw', _this);
23597             },
23598             
23599             onAdd: function()
23600             {
23601                 _this.fireEvent('OverlayViewOnAdd', _this);
23602             },
23603             
23604             onRemove: function()
23605             {
23606                 _this.fireEvent('OverlayViewOnRemove', _this);
23607             },
23608             
23609             show: function(cpx)
23610             {
23611                 _this.fireEvent('OverlayViewShow', _this, cpx);
23612             },
23613             
23614             hide: function()
23615             {
23616                 _this.fireEvent('OverlayViewHide', _this);
23617             }
23618             
23619         });
23620     },
23621     
23622     fromLatLngToContainerPixel: function(event)
23623     {
23624         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23625     },
23626     
23627     isApplied: function() 
23628     {
23629         return this.getGmapContext() == false ? false : true;
23630     },
23631     
23632     getGmapContext: function() 
23633     {
23634         return this.gMapContext
23635     },
23636     
23637     GMapContext: function() 
23638     {
23639         var position = new google.maps.LatLng(this.latitude, this.longitude);
23640         
23641         var _map = new google.maps.Map(this.el.dom, {
23642             center: position,
23643             zoom: this.zoom,
23644             mapTypeId: this.mapTypeId,
23645             mapTypeControl: this.mapTypeControl,
23646             disableDoubleClickZoom: this.disableDoubleClickZoom,
23647             scrollwheel: this.scrollwheel,
23648             streetViewControl: this.streetViewControl,
23649             locationName: this.locationName,
23650             draggable: this.draggable,
23651             enableAutocomplete: this.enableAutocomplete,
23652             enableReverseGeocode: this.enableReverseGeocode
23653         });
23654         
23655         var _marker = new google.maps.Marker({
23656             position: position,
23657             map: _map,
23658             title: this.markerTitle,
23659             draggable: this.draggable
23660         });
23661         
23662         return {
23663             map: _map,
23664             marker: _marker,
23665             circle: null,
23666             location: position,
23667             radius: this.radius,
23668             locationName: this.locationName,
23669             addressComponents: {
23670                 formatted_address: null,
23671                 addressLine1: null,
23672                 addressLine2: null,
23673                 streetName: null,
23674                 streetNumber: null,
23675                 city: null,
23676                 district: null,
23677                 state: null,
23678                 stateOrProvince: null
23679             },
23680             settings: this,
23681             domContainer: this.el.dom,
23682             geodecoder: new google.maps.Geocoder()
23683         };
23684     },
23685     
23686     drawCircle: function(center, radius, options) 
23687     {
23688         if (this.gMapContext.circle != null) {
23689             this.gMapContext.circle.setMap(null);
23690         }
23691         if (radius > 0) {
23692             radius *= 1;
23693             options = Roo.apply({}, options, {
23694                 strokeColor: "#0000FF",
23695                 strokeOpacity: .35,
23696                 strokeWeight: 2,
23697                 fillColor: "#0000FF",
23698                 fillOpacity: .2
23699             });
23700             
23701             options.map = this.gMapContext.map;
23702             options.radius = radius;
23703             options.center = center;
23704             this.gMapContext.circle = new google.maps.Circle(options);
23705             return this.gMapContext.circle;
23706         }
23707         
23708         return null;
23709     },
23710     
23711     setPosition: function(location) 
23712     {
23713         this.gMapContext.location = location;
23714         this.gMapContext.marker.setPosition(location);
23715         this.gMapContext.map.panTo(location);
23716         this.drawCircle(location, this.gMapContext.radius, {});
23717         
23718         var _this = this;
23719         
23720         if (this.gMapContext.settings.enableReverseGeocode) {
23721             this.gMapContext.geodecoder.geocode({
23722                 latLng: this.gMapContext.location
23723             }, function(results, status) {
23724                 
23725                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23726                     _this.gMapContext.locationName = results[0].formatted_address;
23727                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23728                     
23729                     _this.fireEvent('positionchanged', this, location);
23730                 }
23731             });
23732             
23733             return;
23734         }
23735         
23736         this.fireEvent('positionchanged', this, location);
23737     },
23738     
23739     resize: function()
23740     {
23741         google.maps.event.trigger(this.gMapContext.map, "resize");
23742         
23743         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23744         
23745         this.fireEvent('resize', this);
23746     },
23747     
23748     setPositionByLatLng: function(latitude, longitude)
23749     {
23750         this.setPosition(new google.maps.LatLng(latitude, longitude));
23751     },
23752     
23753     getCurrentPosition: function() 
23754     {
23755         return {
23756             latitude: this.gMapContext.location.lat(),
23757             longitude: this.gMapContext.location.lng()
23758         };
23759     },
23760     
23761     getAddressName: function() 
23762     {
23763         return this.gMapContext.locationName;
23764     },
23765     
23766     getAddressComponents: function() 
23767     {
23768         return this.gMapContext.addressComponents;
23769     },
23770     
23771     address_component_from_google_geocode: function(address_components) 
23772     {
23773         var result = {};
23774         
23775         for (var i = 0; i < address_components.length; i++) {
23776             var component = address_components[i];
23777             if (component.types.indexOf("postal_code") >= 0) {
23778                 result.postalCode = component.short_name;
23779             } else if (component.types.indexOf("street_number") >= 0) {
23780                 result.streetNumber = component.short_name;
23781             } else if (component.types.indexOf("route") >= 0) {
23782                 result.streetName = component.short_name;
23783             } else if (component.types.indexOf("neighborhood") >= 0) {
23784                 result.city = component.short_name;
23785             } else if (component.types.indexOf("locality") >= 0) {
23786                 result.city = component.short_name;
23787             } else if (component.types.indexOf("sublocality") >= 0) {
23788                 result.district = component.short_name;
23789             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23790                 result.stateOrProvince = component.short_name;
23791             } else if (component.types.indexOf("country") >= 0) {
23792                 result.country = component.short_name;
23793             }
23794         }
23795         
23796         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23797         result.addressLine2 = "";
23798         return result;
23799     },
23800     
23801     setZoomLevel: function(zoom)
23802     {
23803         this.gMapContext.map.setZoom(zoom);
23804     },
23805     
23806     show: function()
23807     {
23808         if(!this.el){
23809             return;
23810         }
23811         
23812         this.el.show();
23813         
23814         this.resize();
23815         
23816         this.fireEvent('show', this);
23817     },
23818     
23819     hide: function()
23820     {
23821         if(!this.el){
23822             return;
23823         }
23824         
23825         this.el.hide();
23826         
23827         this.fireEvent('hide', this);
23828     }
23829     
23830 });
23831
23832 Roo.apply(Roo.bootstrap.LocationPicker, {
23833     
23834     OverlayView : function(map, options)
23835     {
23836         options = options || {};
23837         
23838         this.setMap(map);
23839     }
23840     
23841     
23842 });/*
23843  * - LGPL
23844  *
23845  * Alert
23846  * 
23847  */
23848
23849 /**
23850  * @class Roo.bootstrap.Alert
23851  * @extends Roo.bootstrap.Component
23852  * Bootstrap Alert class
23853  * @cfg {String} title The title of alert
23854  * @cfg {String} html The content of alert
23855  * @cfg {String} weight (  success | info | warning | danger )
23856  * @cfg {String} faicon font-awesomeicon
23857  * 
23858  * @constructor
23859  * Create a new alert
23860  * @param {Object} config The config object
23861  */
23862
23863
23864 Roo.bootstrap.Alert = function(config){
23865     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23866     
23867 };
23868
23869 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23870     
23871     title: '',
23872     html: '',
23873     weight: false,
23874     faicon: false,
23875     
23876     getAutoCreate : function()
23877     {
23878         
23879         var cfg = {
23880             tag : 'div',
23881             cls : 'alert',
23882             cn : [
23883                 {
23884                     tag : 'i',
23885                     cls : 'roo-alert-icon'
23886                     
23887                 },
23888                 {
23889                     tag : 'b',
23890                     cls : 'roo-alert-title',
23891                     html : this.title
23892                 },
23893                 {
23894                     tag : 'span',
23895                     cls : 'roo-alert-text',
23896                     html : this.html
23897                 }
23898             ]
23899         };
23900         
23901         if(this.faicon){
23902             cfg.cn[0].cls += ' fa ' + this.faicon;
23903         }
23904         
23905         if(this.weight){
23906             cfg.cls += ' alert-' + this.weight;
23907         }
23908         
23909         return cfg;
23910     },
23911     
23912     initEvents: function() 
23913     {
23914         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23915     },
23916     
23917     setTitle : function(str)
23918     {
23919         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23920     },
23921     
23922     setText : function(str)
23923     {
23924         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23925     },
23926     
23927     setWeight : function(weight)
23928     {
23929         if(this.weight){
23930             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23931         }
23932         
23933         this.weight = weight;
23934         
23935         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23936     },
23937     
23938     setIcon : function(icon)
23939     {
23940         if(this.faicon){
23941             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23942         }
23943         
23944         this.faicon = icon
23945         
23946         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23947     },
23948     
23949     hide: function() 
23950     {
23951         this.el.hide();   
23952     },
23953     
23954     show: function() 
23955     {  
23956         this.el.show();   
23957     }
23958     
23959 });
23960
23961  
23962 /*
23963 * Licence: LGPL
23964 */
23965
23966 /**
23967  * @class Roo.bootstrap.UploadCropbox
23968  * @extends Roo.bootstrap.Component
23969  * Bootstrap UploadCropbox class
23970  * @cfg {String} emptyText show when image has been loaded
23971  * @cfg {String} rotateNotify show when image too small to rotate
23972  * @cfg {Number} errorTimeout default 3000
23973  * @cfg {Number} minWidth default 300
23974  * @cfg {Number} minHeight default 300
23975  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
23976  * 
23977  * @constructor
23978  * Create a new UploadCropbox
23979  * @param {Object} config The config object
23980  */
23981
23982 Roo.bootstrap.UploadCropbox = function(config){
23983     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23984     
23985     this.addEvents({
23986         /**
23987          * @event beforeselectfile
23988          * Fire before select file
23989          * @param {Roo.bootstrap.UploadCropbox} this
23990          */
23991         "beforeselectfile" : true,
23992         /**
23993          * @event initial
23994          * Fire after initEvent
23995          * @param {Roo.bootstrap.UploadCropbox} this
23996          */
23997         "initial" : true,
23998         /**
23999          * @event crop
24000          * Fire after initEvent
24001          * @param {Roo.bootstrap.UploadCropbox} this
24002          * @param {String} data
24003          */
24004         "crop" : true,
24005         /**
24006          * @event prepare
24007          * Fire when preparing the file data
24008          * @param {Roo.bootstrap.UploadCropbox} this
24009          * @param {Object} file
24010          */
24011         "prepare" : true,
24012         /**
24013          * @event exception
24014          * Fire when get exception
24015          * @param {Roo.bootstrap.UploadCropbox} this
24016          * @param {Object} options
24017          */
24018         "exception" : true,
24019         /**
24020          * @event beforeloadcanvas
24021          * Fire before load the canvas
24022          * @param {Roo.bootstrap.UploadCropbox} this
24023          * @param {String} src
24024          */
24025         "beforeloadcanvas" : true,
24026         /**
24027          * @event trash
24028          * Fire when trash image
24029          * @param {Roo.bootstrap.UploadCropbox} this
24030          */
24031         "trash" : true,
24032         /**
24033          * @event download
24034          * Fire when download the image
24035          * @param {Roo.bootstrap.UploadCropbox} this
24036          */
24037         "download" : true,
24038         /**
24039          * @event footerbuttonclick
24040          * Fire when footerbuttonclick
24041          * @param {Roo.bootstrap.UploadCropbox} this
24042          * @param {String} type
24043          */
24044         "footerbuttonclick" : true,
24045         /**
24046          * @event resize
24047          * Fire when resize
24048          * @param {Roo.bootstrap.UploadCropbox} this
24049          */
24050         "resize" : true
24051         
24052     });
24053     
24054     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24055 };
24056
24057 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24058     
24059     emptyText : 'Click to upload image',
24060     rotateNotify : 'Image is too small to rotate',
24061     errorTimeout : 3000,
24062     scale : 0,
24063     baseScale : 1,
24064     rotate : 0,
24065     dragable : false,
24066     pinching : false,
24067     mouseX : 0,
24068     mouseY : 0,
24069     cropData : false,
24070     minWidth : 300,
24071     minHeight : 300,
24072     file : false,
24073     exif : {},
24074     baseRotate : 1,
24075     cropType : 'image/jpeg',
24076     buttons : false,
24077     canvasLoaded : false,
24078     
24079     getAutoCreate : function()
24080     {
24081         var cfg = {
24082             tag : 'div',
24083             cls : 'roo-upload-cropbox',
24084             cn : [
24085                 {
24086                     tag : 'div',
24087                     cls : 'roo-upload-cropbox-body',
24088                     style : 'cursor:pointer',
24089                     cn : [
24090                         {
24091                             tag : 'div',
24092                             cls : 'roo-upload-cropbox-preview'
24093                         },
24094                         {
24095                             tag : 'div',
24096                             cls : 'roo-upload-cropbox-thumb'
24097                         },
24098                         {
24099                             tag : 'div',
24100                             cls : 'roo-upload-cropbox-empty-notify',
24101                             html : this.emptyText
24102                         },
24103                         {
24104                             tag : 'div',
24105                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24106                             html : this.rotateNotify
24107                         }
24108                     ]
24109                 },
24110                 {
24111                     tag : 'div',
24112                     cls : 'roo-upload-cropbox-footer',
24113                     cn : {
24114                         tag : 'div',
24115                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24116                         cn : []
24117                     }
24118                 }
24119             ]
24120         };
24121         
24122         return cfg;
24123     },
24124     
24125     onRender : function(ct, position)
24126     {
24127         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24128         
24129         if (this.buttons.length) {
24130             
24131             Roo.each(this.buttons, function(bb) {
24132                 
24133                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24134                 
24135                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24136                 
24137             }, this);
24138         }
24139     },
24140     
24141     initEvents : function()
24142     {
24143         this.urlAPI = (window.createObjectURL && window) || 
24144                                 (window.URL && URL.revokeObjectURL && URL) || 
24145                                 (window.webkitURL && webkitURL);
24146                         
24147         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24148         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24149         
24150         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24151         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24152         
24153         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24154         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24155         this.thumbEl.hide();
24156         
24157         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24158         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24159         
24160         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24161         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24162         this.errorEl.hide();
24163         
24164         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24165         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24166         this.footerEl.hide();
24167         
24168         this.setThumbBoxSize();
24169         
24170         this.bind();
24171         
24172         this.resize();
24173         
24174         this.fireEvent('initial', this);
24175     },
24176
24177     bind : function()
24178     {
24179         var _this = this;
24180         
24181         window.addEventListener("resize", function() { _this.resize(); } );
24182         
24183         this.bodyEl.on('click', this.beforeSelectFile, this);
24184         
24185         if(Roo.isTouch){
24186             this.bodyEl.on('touchstart', this.onTouchStart, this);
24187             this.bodyEl.on('touchmove', this.onTouchMove, this);
24188             this.bodyEl.on('touchend', this.onTouchEnd, this);
24189         }
24190         
24191         if(!Roo.isTouch){
24192             this.bodyEl.on('mousedown', this.onMouseDown, this);
24193             this.bodyEl.on('mousemove', this.onMouseMove, this);
24194             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24195             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24196             Roo.get(document).on('mouseup', this.onMouseUp, this);
24197         }
24198     },
24199     
24200     reset : function()
24201     {    
24202         this.scale = 0;
24203         this.baseScale = 1;
24204         this.rotate = 0;
24205         this.baseRotate = 1;
24206         this.dragable = false;
24207         this.pinching = false;
24208         this.mouseX = 0;
24209         this.mouseY = 0;
24210         this.cropData = false;
24211         this.notifyEl.dom.innerHTML = this.emptyText;
24212         
24213     },
24214     
24215     resize : function()
24216     {
24217         if(this.fireEvent('resize', this) != false){
24218             this.setThumbBoxPosition();
24219             this.setCanvasPosition();
24220         }
24221     },
24222     
24223     onFooterButtonClick : function(e, el, o, type)
24224     {
24225         switch (type) {
24226             case 'rotate-left' :
24227                 this.onRotateLeft(e);
24228                 break;
24229             case 'rotate-right' :
24230                 this.onRotateRight(e);
24231                 break;
24232             case 'picture' :
24233                 this.beforeSelectFile(e);
24234                 break;
24235             case 'trash' :
24236                 this.trash(e);
24237                 break;
24238             case 'crop' :
24239                 this.crop(e);
24240                 break;
24241             case 'download' :
24242                 this.download(e);
24243                 break;
24244             default :
24245                 break;
24246         }
24247         
24248         this.fireEvent('footerbuttonclick', this, type);
24249     },
24250     
24251     beforeSelectFile : function(e)
24252     {
24253         this.fireEvent('beforeselectfile', this);
24254     },
24255     
24256     trash : function(e)
24257     {
24258         this.fireEvent('trash', this);
24259     },
24260     
24261     download : function(e)
24262     {
24263         this.fireEvent('download', this);
24264     },
24265     
24266     loadCanvas : function(src)
24267     {   
24268         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24269             
24270             this.reset();
24271             
24272             this.imageEl = document.createElement('img');
24273             
24274             var _this = this;
24275             
24276             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24277             
24278             this.imageEl.src = src;
24279         }
24280     },
24281     
24282     onLoadCanvas : function()
24283     {   
24284         this.bodyEl.un('click', this.beforeSelectFile, this);
24285         
24286         this.notifyEl.hide();
24287         this.thumbEl.show();
24288         this.footerEl.show();
24289         
24290         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24291         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24292         
24293         this.setThumbBoxPosition();
24294         this.baseRotateLevel();
24295         this.baseScaleLevel();
24296         
24297         this.draw();
24298         
24299         this.resize();
24300         
24301         this.canvasLoaded = true;
24302         
24303     },
24304     
24305     setCanvasPosition : function()
24306     {   
24307         if(!this.canvasEl){
24308             return;
24309         }
24310         
24311         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24312         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24313         
24314         this.previewEl.setLeft(pw);
24315         this.previewEl.setTop(ph);
24316         
24317     },
24318     
24319     onMouseDown : function(e)
24320     {   
24321         e.stopEvent();
24322         
24323         this.dragable = true;
24324         this.pinching = false;
24325         
24326         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24327         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24328         
24329     },
24330     
24331     onMouseMove : function(e)
24332     {   
24333         e.stopEvent();
24334         
24335         if(!this.canvasLoaded){
24336             return;
24337         }
24338         
24339         if (!this.dragable){
24340             return;
24341         }
24342         
24343         var minX = Math.ceil(this.thumbEl.getLeft(true));
24344         var minY = Math.ceil(this.thumbEl.getTop(true));
24345         
24346         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24347         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24348         
24349         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24350         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24351         
24352         x = x - this.mouseX;
24353         y = y - this.mouseY;
24354         
24355         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24356         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24357         
24358         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24359         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24360         
24361         this.previewEl.setLeft(bgX);
24362         this.previewEl.setTop(bgY);
24363         
24364         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24365         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24366     },
24367     
24368     onMouseUp : function(e)
24369     {   
24370         e.stopEvent();
24371         
24372         this.dragable = false;
24373     },
24374     
24375     onMouseWheel : function(e)
24376     {   
24377         e.stopEvent();
24378         
24379         this.startScale = this.scale;
24380         
24381         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24382         
24383         if(!this.zoomable()){
24384             this.scale = this.startScale;
24385             return;
24386         }
24387         
24388         this.draw();
24389         
24390         return;
24391     },
24392     
24393     zoomable : function()
24394     {
24395         var minScale = this.thumbEl.getWidth() / this.minWidth;
24396         
24397         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel());
24398         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel());
24399         
24400         if(
24401                 (this.rotate == 0 || this.rotate == 180) && 
24402                 (
24403                     width / minScale < this.minWidth || 
24404                     width / minScale > this.imageEl.OriginWidth || 
24405                     height / minScale < this.minHeight || 
24406                     height / minScale > this.imageEl.OriginHeight
24407                 )
24408         ){
24409             return false;
24410         }
24411         
24412         if(
24413                 (this.rotate == 90 || this.rotate == 270) && 
24414                 (
24415                     width / minScale < this.minHeight || 
24416                     width / minScale > this.imageEl.OriginWidth || 
24417                     height / minScale < this.minWidth || 
24418                     height / minScale > this.imageEl.OriginHeight
24419                 )
24420         ){
24421             return false;
24422         }
24423         
24424         return true;
24425         
24426     },
24427     
24428     onRotateLeft : function(e)
24429     {   
24430         var minScale = this.thumbEl.getWidth() / this.minWidth;
24431         
24432         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24433             
24434             var bw = this.canvasEl.width / this.getScaleLevel();
24435             var bh = this.canvasEl.height / this.getScaleLevel();
24436             
24437             this.startScale = this.scale;
24438             
24439             while (this.getScaleLevel() < minScale){
24440             
24441                 this.scale = this.scale + 1;
24442                 
24443                 if(!this.zoomable()){
24444                     break;
24445                 }
24446                 
24447                 if(
24448                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24449                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
24450                 ){
24451                     continue;
24452                 }
24453                 
24454                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24455
24456                 this.draw();
24457                 
24458                 return;
24459             }
24460             
24461             this.scale = this.startScale;
24462             
24463             this.onRotateFail();
24464             
24465             return false;
24466         }
24467         
24468         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24469
24470         this.draw();
24471         
24472     },
24473     
24474     onRotateRight : function(e)
24475     {
24476         var minScale = this.thumbEl.getWidth() / this.minWidth;
24477         
24478         if(this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight()){
24479             
24480             var bw = this.canvasEl.width / this.getScaleLevel();
24481             var bh = this.canvasEl.height / this.getScaleLevel();
24482             
24483             this.startScale = this.scale;
24484             
24485             while (this.getScaleLevel() < minScale){
24486             
24487                 this.scale = this.scale + 1;
24488                 
24489                 if(!this.zoomable()){
24490                     break;
24491                 }
24492                 
24493                 if(
24494                         bw * this.getScaleLevel() < this.thumbEl.getHeight() ||
24495                         bh * this.getScaleLevel() < this.thumbEl.getWidth()
24496                 ){
24497                     continue;
24498                 }
24499                 
24500                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24501
24502                 this.draw();
24503                 
24504                 return;
24505             }
24506             
24507             this.scale = this.startScale;
24508             
24509             this.onRotateFail();
24510             
24511             return false;
24512         }
24513         
24514         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24515
24516         this.draw();
24517     },
24518     
24519     onRotateFail : function()
24520     {
24521         this.errorEl.show(true);
24522         
24523         var _this = this;
24524         
24525         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24526     },
24527     
24528     draw : function()
24529     {
24530         this.previewEl.dom.innerHTML = '';
24531         
24532         var canvasEl = document.createElement("canvas");
24533         
24534         var contextEl = canvasEl.getContext("2d");
24535         
24536         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24537         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24538         var center = this.imageEl.OriginWidth / 2;
24539         
24540         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24541             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24542             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24543             center = this.imageEl.OriginHeight / 2;
24544         }
24545         
24546         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24547         
24548         contextEl.translate(center, center);
24549         contextEl.rotate(this.rotate * Math.PI / 180);
24550
24551         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24552         
24553         this.canvasEl = document.createElement("canvas");
24554         
24555         this.contextEl = this.canvasEl.getContext("2d");
24556         
24557         switch (this.rotate) {
24558             case 0 :
24559                 
24560                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24561                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24562                 
24563                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24564                 
24565                 break;
24566             case 90 : 
24567                 
24568                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24569                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24570                 
24571                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24572                     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);
24573                     break;
24574                 }
24575                 
24576                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24577                 
24578                 break;
24579             case 180 :
24580                 
24581                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24582                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24583                 
24584                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24585                     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);
24586                     break;
24587                 }
24588                 
24589                 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);
24590                 
24591                 break;
24592             case 270 :
24593                 
24594                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24595                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24596         
24597                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24598                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24599                     break;
24600                 }
24601                 
24602                 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);
24603                 
24604                 break;
24605             default : 
24606                 break;
24607         }
24608         
24609         this.previewEl.appendChild(this.canvasEl);
24610         
24611         this.setCanvasPosition();
24612     },
24613     
24614     crop : function()
24615     {
24616         if(!this.canvasLoaded){
24617             return;
24618         }
24619         var canvas = document.createElement("canvas");
24620         
24621         var context = canvas.getContext("2d");
24622         
24623         canvas.width = this.minWidth;
24624         canvas.height = this.minHeight;
24625         
24626         var cropWidth = this.thumbEl.getWidth();
24627         var cropHeight = this.thumbEl.getHeight();
24628         
24629         var x = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
24630         var y = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
24631         
24632         if(this.canvasEl.width - cropWidth < x){
24633             x = this.canvasEl.width - cropWidth;
24634         }
24635         
24636         if(this.canvasEl.height - cropHeight < y){
24637             y = this.canvasEl.height - cropHeight;
24638         }
24639         
24640         x = x < 0 ? 0 : x;
24641         y = y < 0 ? 0 : y;
24642         
24643         context.drawImage(this.canvasEl, x, y, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
24644         
24645         this.cropData = canvas.toDataURL(this.cropType);
24646         
24647         this.fireEvent('crop', this, this.cropData);
24648         
24649     },
24650     
24651     setThumbBoxSize : function()
24652     {
24653         var height = 300;
24654         var width = Math.ceil(this.minWidth * height / this.minHeight);
24655         
24656         if(this.minWidth > this.minHeight){
24657             width = 300;
24658             height = Math.ceil(this.minHeight * width / this.minWidth);
24659         }
24660         
24661         this.thumbEl.setStyle({
24662             width : width + 'px',
24663             height : height + 'px'
24664         });
24665
24666         return;
24667             
24668     },
24669     
24670     setThumbBoxPosition : function()
24671     {
24672         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
24673         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
24674         
24675         this.thumbEl.setLeft(x);
24676         this.thumbEl.setTop(y);
24677         
24678     },
24679     
24680     baseRotateLevel : function()
24681     {
24682         this.baseRotate = 1;
24683         
24684         if(
24685                 typeof(this.exif) != 'undefined' &&
24686                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
24687                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
24688         ){
24689             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24690         }
24691         
24692         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
24693         
24694     },
24695     
24696     baseScaleLevel : function()
24697     {
24698         var width, height;
24699         
24700         if(this.baseRotate == 6 || this.baseRotate == 8){
24701             
24702             width = this.thumbEl.getHeight();
24703             this.baseScale = height / this.imageEl.OriginHeight;
24704             
24705             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
24706                 height = this.thumbEl.getWidth();
24707                 this.baseScale = height / this.imageEl.OriginHeight;
24708             }
24709             
24710             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24711                 height = this.thumbEl.getWidth();
24712                 this.baseScale = height / this.imageEl.OriginHeight;
24713                 
24714                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
24715                     width = this.thumbEl.getHeight();
24716                     this.baseScale = width / this.imageEl.OriginWidth;
24717                 }
24718             }
24719             
24720             return;
24721         }
24722         
24723         width = this.thumbEl.getWidth();
24724         this.baseScale = width / this.imageEl.OriginWidth;
24725         
24726         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
24727             height = this.thumbEl.getHeight();
24728             this.baseScale = height / this.imageEl.OriginHeight;
24729         }
24730         
24731         
24732         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24733             
24734             height = this.thumbEl.getHeight();
24735             this.baseScale = height / this.imageEl.OriginHeight;
24736             
24737             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
24738                 width = this.thumbEl.getWidth();
24739                 this.baseScale = width / this.imageEl.OriginWidth;
24740             }
24741             
24742         }
24743         
24744         return;
24745     },
24746     
24747     getScaleLevel : function()
24748     {
24749         return this.baseScale * Math.pow(1.1, this.scale);
24750     },
24751     
24752     onTouchStart : function(e)
24753     {
24754         if(!this.canvasLoaded){
24755             this.beforeSelectFile(e);
24756             return;
24757         }
24758         
24759         var touches = e.browserEvent.touches;
24760         
24761         if(!touches){
24762             return;
24763         }
24764         
24765         if(touches.length == 1){
24766             this.onMouseDown(e);
24767             return;
24768         }
24769         
24770         if(touches.length != 2){
24771             return;
24772         }
24773         
24774         var coords = [];
24775         
24776         for(var i = 0, finger; finger = touches[i]; i++){
24777             coords.push(finger.pageX, finger.pageY);
24778         }
24779         
24780         var x = Math.pow(coords[0] - coords[2], 2);
24781         var y = Math.pow(coords[1] - coords[3], 2);
24782         
24783         this.startDistance = Math.sqrt(x + y);
24784         
24785         this.startScale = this.scale;
24786         
24787         this.pinching = true;
24788         this.dragable = false;
24789         
24790     },
24791     
24792     onTouchMove : function(e)
24793     {
24794         if(!this.pinching && !this.dragable){
24795             return;
24796         }
24797         
24798         var touches = e.browserEvent.touches;
24799         
24800         if(!touches){
24801             return;
24802         }
24803         
24804         if(this.dragable){
24805             this.onMouseMove(e);
24806             return;
24807         }
24808         
24809         var coords = [];
24810         
24811         for(var i = 0, finger; finger = touches[i]; i++){
24812             coords.push(finger.pageX, finger.pageY);
24813         }
24814         
24815         var x = Math.pow(coords[0] - coords[2], 2);
24816         var y = Math.pow(coords[1] - coords[3], 2);
24817         
24818         this.endDistance = Math.sqrt(x + y);
24819         
24820         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24821         
24822         if(!this.zoomable()){
24823             this.scale = this.startScale;
24824             return;
24825         }
24826         
24827         this.draw();
24828         
24829     },
24830     
24831     onTouchEnd : function(e)
24832     {
24833         this.pinching = false;
24834         this.dragable = false;
24835         
24836     },
24837     
24838     prepare : function(input)
24839     {        
24840         this.file = false;
24841         this.exif = {};
24842         
24843         if(typeof(input) === 'string'){
24844             this.loadCanvas(input);
24845             return;
24846         }
24847         
24848         if(!input.files || !input.files[0] || !this.urlAPI){
24849             return;
24850         }
24851         
24852         this.file = input.files[0];
24853         this.cropType = this.file.type;
24854         
24855         var _this = this;
24856         
24857         if(this.fireEvent('prepare', this, this.file) != false){
24858             
24859             var reader = new FileReader();
24860             
24861             reader.onload = function (e) {
24862                 if (e.target.error) {
24863                     Roo.log(e.target.error);
24864                     return;
24865                 }
24866                 
24867                 var buffer = e.target.result,
24868                     dataView = new DataView(buffer),
24869                     offset = 2,
24870                     maxOffset = dataView.byteLength - 4,
24871                     markerBytes,
24872                     markerLength;
24873                 
24874                 if (dataView.getUint16(0) === 0xffd8) {
24875                     while (offset < maxOffset) {
24876                         markerBytes = dataView.getUint16(offset);
24877                         
24878                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
24879                             markerLength = dataView.getUint16(offset + 2) + 2;
24880                             if (offset + markerLength > dataView.byteLength) {
24881                                 Roo.log('Invalid meta data: Invalid segment size.');
24882                                 break;
24883                             }
24884                             
24885                             if(markerBytes == 0xffe1){
24886                                 _this.parseExifData(
24887                                     dataView,
24888                                     offset,
24889                                     markerLength
24890                                 );
24891                             }
24892                             
24893                             offset += markerLength;
24894                             
24895                             continue;
24896                         }
24897                         
24898                         break;
24899                     }
24900                     
24901                 }
24902                 
24903                 var url = _this.urlAPI.createObjectURL(_this.file);
24904                 
24905                 _this.loadCanvas(url);
24906                 
24907                 return;
24908             }
24909             
24910             reader.readAsArrayBuffer(this.file);
24911             
24912         }
24913         
24914     },
24915     
24916     parseExifData : function(dataView, offset, length)
24917     {
24918         var tiffOffset = offset + 10,
24919             littleEndian,
24920             dirOffset;
24921     
24922         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24923             // No Exif data, might be XMP data instead
24924             return;
24925         }
24926         
24927         // Check for the ASCII code for "Exif" (0x45786966):
24928         if (dataView.getUint32(offset + 4) !== 0x45786966) {
24929             // No Exif data, might be XMP data instead
24930             return;
24931         }
24932         if (tiffOffset + 8 > dataView.byteLength) {
24933             Roo.log('Invalid Exif data: Invalid segment size.');
24934             return;
24935         }
24936         // Check for the two null bytes:
24937         if (dataView.getUint16(offset + 8) !== 0x0000) {
24938             Roo.log('Invalid Exif data: Missing byte alignment offset.');
24939             return;
24940         }
24941         // Check the byte alignment:
24942         switch (dataView.getUint16(tiffOffset)) {
24943         case 0x4949:
24944             littleEndian = true;
24945             break;
24946         case 0x4D4D:
24947             littleEndian = false;
24948             break;
24949         default:
24950             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
24951             return;
24952         }
24953         // Check for the TIFF tag marker (0x002A):
24954         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
24955             Roo.log('Invalid Exif data: Missing TIFF marker.');
24956             return;
24957         }
24958         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
24959         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
24960         
24961         this.parseExifTags(
24962             dataView,
24963             tiffOffset,
24964             tiffOffset + dirOffset,
24965             littleEndian
24966         );
24967     },
24968     
24969     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
24970     {
24971         var tagsNumber,
24972             dirEndOffset,
24973             i;
24974         if (dirOffset + 6 > dataView.byteLength) {
24975             Roo.log('Invalid Exif data: Invalid directory offset.');
24976             return;
24977         }
24978         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
24979         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
24980         if (dirEndOffset + 4 > dataView.byteLength) {
24981             Roo.log('Invalid Exif data: Invalid directory size.');
24982             return;
24983         }
24984         for (i = 0; i < tagsNumber; i += 1) {
24985             this.parseExifTag(
24986                 dataView,
24987                 tiffOffset,
24988                 dirOffset + 2 + 12 * i, // tag offset
24989                 littleEndian
24990             );
24991         }
24992         // Return the offset to the next directory:
24993         return dataView.getUint32(dirEndOffset, littleEndian);
24994     },
24995     
24996     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
24997     {
24998         var tag = dataView.getUint16(offset, littleEndian);
24999         
25000         this.exif[tag] = this.getExifValue(
25001             dataView,
25002             tiffOffset,
25003             offset,
25004             dataView.getUint16(offset + 2, littleEndian), // tag type
25005             dataView.getUint32(offset + 4, littleEndian), // tag length
25006             littleEndian
25007         );
25008     },
25009     
25010     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25011     {
25012         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25013             tagSize,
25014             dataOffset,
25015             values,
25016             i,
25017             str,
25018             c;
25019     
25020         if (!tagType) {
25021             Roo.log('Invalid Exif data: Invalid tag type.');
25022             return;
25023         }
25024         
25025         tagSize = tagType.size * length;
25026         // Determine if the value is contained in the dataOffset bytes,
25027         // or if the value at the dataOffset is a pointer to the actual data:
25028         dataOffset = tagSize > 4 ?
25029                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25030         if (dataOffset + tagSize > dataView.byteLength) {
25031             Roo.log('Invalid Exif data: Invalid data offset.');
25032             return;
25033         }
25034         if (length === 1) {
25035             return tagType.getValue(dataView, dataOffset, littleEndian);
25036         }
25037         values = [];
25038         for (i = 0; i < length; i += 1) {
25039             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25040         }
25041         
25042         if (tagType.ascii) {
25043             str = '';
25044             // Concatenate the chars:
25045             for (i = 0; i < values.length; i += 1) {
25046                 c = values[i];
25047                 // Ignore the terminating NULL byte(s):
25048                 if (c === '\u0000') {
25049                     break;
25050                 }
25051                 str += c;
25052             }
25053             return str;
25054         }
25055         return values;
25056     }
25057     
25058 });
25059
25060 Roo.apply(Roo.bootstrap.UploadCropbox, {
25061     tags : {
25062         'Orientation': 0x0112
25063     },
25064     
25065     Orientation: {
25066             1: 0, //'top-left',
25067 //            2: 'top-right',
25068             3: 180, //'bottom-right',
25069 //            4: 'bottom-left',
25070 //            5: 'left-top',
25071             6: 90, //'right-top',
25072 //            7: 'right-bottom',
25073             8: 270 //'left-bottom'
25074     },
25075     
25076     exifTagTypes : {
25077         // byte, 8-bit unsigned int:
25078         1: {
25079             getValue: function (dataView, dataOffset) {
25080                 return dataView.getUint8(dataOffset);
25081             },
25082             size: 1
25083         },
25084         // ascii, 8-bit byte:
25085         2: {
25086             getValue: function (dataView, dataOffset) {
25087                 return String.fromCharCode(dataView.getUint8(dataOffset));
25088             },
25089             size: 1,
25090             ascii: true
25091         },
25092         // short, 16 bit int:
25093         3: {
25094             getValue: function (dataView, dataOffset, littleEndian) {
25095                 return dataView.getUint16(dataOffset, littleEndian);
25096             },
25097             size: 2
25098         },
25099         // long, 32 bit int:
25100         4: {
25101             getValue: function (dataView, dataOffset, littleEndian) {
25102                 return dataView.getUint32(dataOffset, littleEndian);
25103             },
25104             size: 4
25105         },
25106         // rational = two long values, first is numerator, second is denominator:
25107         5: {
25108             getValue: function (dataView, dataOffset, littleEndian) {
25109                 return dataView.getUint32(dataOffset, littleEndian) /
25110                     dataView.getUint32(dataOffset + 4, littleEndian);
25111             },
25112             size: 8
25113         },
25114         // slong, 32 bit signed int:
25115         9: {
25116             getValue: function (dataView, dataOffset, littleEndian) {
25117                 return dataView.getInt32(dataOffset, littleEndian);
25118             },
25119             size: 4
25120         },
25121         // srational, two slongs, first is numerator, second is denominator:
25122         10: {
25123             getValue: function (dataView, dataOffset, littleEndian) {
25124                 return dataView.getInt32(dataOffset, littleEndian) /
25125                     dataView.getInt32(dataOffset + 4, littleEndian);
25126             },
25127             size: 8
25128         }
25129     },
25130     
25131     footer : {
25132         STANDARD : [
25133             {
25134                 tag : 'div',
25135                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25136                 action : 'rotate-left',
25137                 cn : [
25138                     {
25139                         tag : 'button',
25140                         cls : 'btn btn-default',
25141                         html : '<i class="fa fa-undo"></i>'
25142                     }
25143                 ]
25144             },
25145             {
25146                 tag : 'div',
25147                 cls : 'btn-group roo-upload-cropbox-picture',
25148                 action : 'picture',
25149                 cn : [
25150                     {
25151                         tag : 'button',
25152                         cls : 'btn btn-default',
25153                         html : '<i class="fa fa-picture-o"></i>'
25154                     }
25155                 ]
25156             },
25157             {
25158                 tag : 'div',
25159                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25160                 action : 'rotate-right',
25161                 cn : [
25162                     {
25163                         tag : 'button',
25164                         cls : 'btn btn-default',
25165                         html : '<i class="fa fa-repeat"></i>'
25166                     }
25167                 ]
25168             }
25169         ],
25170         DOCUMENT : [
25171             {
25172                 tag : 'div',
25173                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25174                 action : 'rotate-left',
25175                 cn : [
25176                     {
25177                         tag : 'button',
25178                         cls : 'btn btn-default',
25179                         html : '<i class="fa fa-undo"></i>'
25180                     }
25181                 ]
25182             },
25183             {
25184                 tag : 'div',
25185                 cls : 'btn-group roo-upload-cropbox-download',
25186                 action : 'download',
25187                 cn : [
25188                     {
25189                         tag : 'button',
25190                         cls : 'btn btn-default',
25191                         html : '<i class="fa fa-download"></i>'
25192                     }
25193                 ]
25194             },
25195             {
25196                 tag : 'div',
25197                 cls : 'btn-group roo-upload-cropbox-crop',
25198                 action : 'crop',
25199                 cn : [
25200                     {
25201                         tag : 'button',
25202                         cls : 'btn btn-default',
25203                         html : '<i class="fa fa-crop"></i>'
25204                     }
25205                 ]
25206             },
25207             {
25208                 tag : 'div',
25209                 cls : 'btn-group roo-upload-cropbox-trash',
25210                 action : 'trash',
25211                 cn : [
25212                     {
25213                         tag : 'button',
25214                         cls : 'btn btn-default',
25215                         html : '<i class="fa fa-trash"></i>'
25216                     }
25217                 ]
25218             },
25219             {
25220                 tag : 'div',
25221                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25222                 action : 'rotate-right',
25223                 cn : [
25224                     {
25225                         tag : 'button',
25226                         cls : 'btn btn-default',
25227                         html : '<i class="fa fa-repeat"></i>'
25228                     }
25229                 ]
25230             }
25231         ]
25232     }
25233 });
25234
25235 /*
25236 * Licence: LGPL
25237 */
25238
25239 /**
25240  * @class Roo.bootstrap.DocumentManager
25241  * @extends Roo.bootstrap.Component
25242  * Bootstrap DocumentManager class
25243  * @cfg {String} paramName default 'imageUpload'
25244  * @cfg {String} method default POST
25245  * @cfg {String} url action url
25246  * @cfg {Number} boxes number of boxes default 12
25247  * @cfg {Boolean} multiple multiple upload default true
25248  * @cfg {Number} minWidth default 300
25249  * @cfg {Number} minHeight default 300
25250  * @cfg {Number} thumbSize default 300
25251  * @cfg {String} fieldLabel
25252  * @cfg {Number} labelWidth default 4
25253  * @cfg {String} labelAlign (left|top) default left
25254  * 
25255  * @constructor
25256  * Create a new DocumentManager
25257  * @param {Object} config The config object
25258  */
25259
25260 Roo.bootstrap.DocumentManager = function(config){
25261     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25262     
25263     this.addEvents({
25264         /**
25265          * @event initial
25266          * Fire when initial the DocumentManager
25267          * @param {Roo.bootstrap.DocumentManager} this
25268          */
25269         "initial" : true,
25270         /**
25271          * @event inspect
25272          * inspect selected file
25273          * @param {Roo.bootstrap.DocumentManager} this
25274          * @param {File} file
25275          */
25276         "inspect" : true,
25277         /**
25278          * @event exception
25279          * Fire when xhr load exception
25280          * @param {Roo.bootstrap.DocumentManager} this
25281          * @param {XMLHttpRequest} xhr
25282          */
25283         "exception" : true,
25284         /**
25285          * @event prepare
25286          * prepare the form data
25287          * @param {Roo.bootstrap.DocumentManager} this
25288          * @param {Object} formData
25289          */
25290         "prepare" : true,
25291         /**
25292          * @event remove
25293          * Fire when remove the file
25294          * @param {Roo.bootstrap.DocumentManager} this
25295          * @param {Object} file
25296          */
25297         "remove" : true,
25298         /**
25299          * @event refresh
25300          * Fire after refresh the file
25301          * @param {Roo.bootstrap.DocumentManager} this
25302          */
25303         "refresh" : true,
25304         /**
25305          * @event click
25306          * Fire after click the image
25307          * @param {Roo.bootstrap.DocumentManager} this
25308          * @param {Object} file
25309          */
25310         "click" : true
25311         
25312     });
25313 };
25314
25315 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25316     
25317     boxes : 12,
25318     inputName : '',
25319     minWidth : 300,
25320     minHeight : 300,
25321     thumbSize : 300,
25322     multiple : true,
25323     files : [],
25324     method : 'POST',
25325     url : '',
25326     paramName : 'imageUpload',
25327     fieldLabel : '',
25328     labelWidth : 4,
25329     labelAlign : 'left',
25330     
25331     getAutoCreate : function()
25332     {   
25333         var managerWidget = {
25334             tag : 'div',
25335             cls : 'roo-document-manager',
25336             cn : [
25337                 {
25338                     tag : 'input',
25339                     cls : 'roo-document-manager-selector',
25340                     type : 'file'
25341                 },
25342                 {
25343                     tag : 'div',
25344                     cls : 'roo-document-manager-uploader',
25345                     cn : [
25346                         {
25347                             tag : 'div',
25348                             cls : 'roo-document-manager-upload-btn',
25349                             html : '<i class="fa fa-plus"></i>'
25350                         }
25351                     ]
25352                     
25353                 }
25354             ]
25355         };
25356         
25357         var content = [
25358             {
25359                 tag : 'div',
25360                 cls : 'column col-md-12',
25361                 cn : managerWidget
25362             }
25363         ];
25364         
25365         if(this.fieldLabel.length){
25366             
25367             content = [
25368                 {
25369                     tag : 'div',
25370                     cls : 'column col-md-12',
25371                     html : this.fieldLabel
25372                 },
25373                 {
25374                     tag : 'div',
25375                     cls : 'column col-md-12',
25376                     cn : managerWidget
25377                 }
25378             ];
25379
25380             if(this.labelAlign == 'left'){
25381                 content = [
25382                     {
25383                         tag : 'div',
25384                         cls : 'column col-md-' + this.labelWidth,
25385                         html : this.fieldLabel
25386                     },
25387                     {
25388                         tag : 'div',
25389                         cls : 'column col-md-' + (12 - this.labelWidth),
25390                         cn : managerWidget
25391                     }
25392                 ];
25393                 
25394             }
25395         }
25396         
25397         var cfg = {
25398             tag : 'div',
25399             cls : 'row clearfix',
25400             cn : content
25401         };
25402         
25403         return cfg;
25404         
25405     },
25406     
25407     initEvents : function()
25408     {
25409         this.managerEl = this.el.select('.roo-document-manager', true).first();
25410         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25411         
25412         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25413         this.selectorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25414         this.selectorEl.hide();
25415         
25416         if(this.multiple){
25417             this.selectorEl.attr('multiple', 'multiple');
25418         }
25419         
25420         this.selectorEl.on('change', this.onSelect, this);
25421         
25422         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25423         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25424         
25425         this.uploader.on('click', this.onUpload, this);
25426         
25427         var _this = this;
25428         
25429         window.addEventListener("resize", function() { _this.refresh(); } );
25430         
25431         this.fireEvent('initial', this);
25432     },
25433     
25434     onUpload : function(e)
25435     {
25436         e.preventDefault();
25437         
25438         this.selectorEl.dom.click();
25439         
25440     },
25441     
25442     onSelect : function(e)
25443     {
25444         e.preventDefault();
25445         
25446         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25447             return;
25448         }
25449         
25450         Roo.each(this.selectorEl.dom.files, function(file){
25451             if(this.fireEvent('inspect', this, file) != false){
25452                 this.files.push(file);
25453             }
25454         }, this);
25455         
25456         this.process();
25457         
25458     },
25459     
25460     process : function()
25461     {
25462         this.selectorEl.dom.value = '';
25463         
25464         if(!this.files.length){
25465             return;
25466         }
25467         
25468         if(this.files.length > this.boxes){
25469             this.files = this.files.slice(0, this.boxes);
25470         }
25471         
25472         var xhr = new XMLHttpRequest();
25473         
25474         Roo.each(this.files, function(file, index){
25475             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25476                 return;
25477             }
25478             
25479             file.xhr = xhr;
25480             
25481             this.managerEl.createChild({
25482                 tag : 'div',
25483                 cls : 'roo-document-manager-loading',
25484                 cn : [
25485                     {
25486                         tag : 'div',
25487                         tooltip : file.name,
25488                         cls : 'roo-document-manager-thumb',
25489                         html : '<i class="fa fa-spinner fa-pulse"></i>'
25490                     }
25491                 ]
25492
25493             });
25494             
25495         }, this);
25496         
25497         if(this.files.length > this.boxes - 1 ){
25498             this.uploader.hide();
25499         }
25500         
25501         var headers = {
25502             "Accept": "application/json",
25503             "Cache-Control": "no-cache",
25504             "X-Requested-With": "XMLHttpRequest"
25505         };
25506         
25507         xhr.open(this.method, this.url, true);
25508         
25509         for (var headerName in headers) {
25510             var headerValue = headers[headerName];
25511             if (headerValue) {
25512                 xhr.setRequestHeader(headerName, headerValue);
25513             }
25514         }
25515         
25516         var _this = this;
25517         
25518         xhr.onload = function()
25519         {
25520             _this.xhrOnLoad(xhr);
25521         }
25522         
25523         xhr.onerror = function()
25524         {
25525             _this.xhrOnError(xhr);
25526         }
25527         
25528         var formData = new FormData();
25529
25530         formData.append('returnHTML', 'NO');
25531         
25532         Roo.each(this.files, function(file, index){
25533             
25534             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25535                 return;
25536             }
25537             
25538             formData.append(this.getParamName(index), file, file.name);
25539             
25540         }, this);
25541         
25542         if(this.fireEvent('prepare', this, formData) != false){
25543             xhr.send(formData);
25544         };
25545         
25546     },
25547     
25548     getParamName : function(i)
25549     {
25550         if(!this.multiple){
25551             return this.paramName;
25552         }
25553         
25554         return this.paramName + "_" + i;
25555     },
25556     
25557     refresh : function()
25558     {
25559         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
25560             el.remove();
25561         }, this);
25562         
25563         
25564         var files = [];
25565         
25566         Roo.each(this.files, function(file){
25567             
25568             if(typeof(file.id) == 'undefined' || file.id * 1 < 1){
25569                 return;
25570             }
25571             
25572             if(file.target){
25573                 files.push(file);
25574                 return;
25575             }
25576             
25577             var previewEl = this.managerEl.createChild({
25578                 tag : 'div',
25579                 cls : 'roo-document-manager-preview',
25580                 cn : [
25581                     {
25582                         tag : 'div',
25583                         tooltip : file.filename,
25584                         cls : 'roo-document-manager-thumb',
25585                         html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
25586                     },
25587                     {
25588                         tag : 'button',
25589                         cls : 'close',
25590                         html : 'x'
25591                     }
25592                 ]
25593             });
25594             
25595             var close = previewEl.select('button.close', true).first();
25596             
25597             close.on('click', this.onRemove, this, file);
25598             
25599             file.target = previewEl;
25600             
25601             var image = previewEl.select('img', true).first();
25602             
25603             image.on('click', this.onClick, this, file);
25604             
25605             files.push(file);
25606             
25607             return;
25608             
25609         }, this);
25610         
25611         this.files = files;
25612         
25613         this.uploader.show();
25614         
25615         if(this.files.length > this.boxes - 1){
25616             this.uploader.hide();
25617         }
25618         
25619         Roo.isTouch ? this.closable(false) : this.closable(true);
25620         
25621         this.fireEvent('refresh', this);
25622     },
25623     
25624     onRemove : function(e, el, o)
25625     {
25626         e.preventDefault();
25627         
25628         this.fireEvent('remove', this, o);
25629         
25630     },
25631     
25632     remove : function(o)
25633     {
25634         var files = [];
25635         
25636         Roo.each(this.files, function(file){
25637             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
25638                 files.push(file);
25639                 return;
25640             }
25641
25642             o.target.remove();
25643
25644         }, this);
25645         
25646         this.files = files;
25647         
25648         this.refresh();
25649     },
25650     
25651     onClick : function(e, el, o)
25652     {
25653         e.preventDefault();
25654         
25655         this.fireEvent('click', this, o);
25656         
25657     },
25658     
25659     closable : function(closable)
25660     {
25661         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
25662             
25663             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25664             
25665             if(closable){
25666                 el.show();
25667                 return;
25668             }
25669             
25670             el.hide();
25671             
25672         }, this);
25673     },
25674     
25675     xhrOnLoad : function(xhr)
25676     {
25677         if (xhr.readyState !== 4) {
25678             this.refresh();
25679             this.fireEvent('exception', this, xhr);
25680             return;
25681         }
25682
25683         var response = Roo.decode(xhr.responseText);
25684         
25685         if(!response.success){
25686             this.refresh();
25687             this.fireEvent('exception', this, xhr);
25688             return;
25689         }
25690         
25691         var i = 0;
25692         
25693         Roo.each(this.files, function(file, index){
25694             
25695             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
25696                 return;
25697             }
25698             
25699             this.files[index] = response.data[i];
25700             i++;
25701             
25702             return;
25703             
25704         }, this);
25705         
25706         this.refresh();
25707         
25708     },
25709     
25710     xhrOnError : function()
25711     {
25712         Roo.log('xhr on error');
25713         
25714         var response = Roo.decode(xhr.responseText);
25715           
25716         Roo.log(response);
25717     }
25718     
25719     
25720     
25721 });
25722 /*
25723 * Licence: LGPL
25724 */
25725
25726 /**
25727  * @class Roo.bootstrap.DocumentViewer
25728  * @extends Roo.bootstrap.Component
25729  * Bootstrap DocumentViewer class
25730  * 
25731  * @constructor
25732  * Create a new DocumentViewer
25733  * @param {Object} config The config object
25734  */
25735
25736 Roo.bootstrap.DocumentViewer = function(config){
25737     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
25738     
25739     this.addEvents({
25740         /**
25741          * @event initial
25742          * Fire after initEvent
25743          * @param {Roo.bootstrap.DocumentViewer} this
25744          */
25745         "initial" : true,
25746         /**
25747          * @event click
25748          * Fire after click
25749          * @param {Roo.bootstrap.DocumentViewer} this
25750          */
25751         "click" : true,
25752         /**
25753          * @event trash
25754          * Fire after trash button
25755          * @param {Roo.bootstrap.DocumentViewer} this
25756          */
25757         "trash" : true
25758         
25759     });
25760 };
25761
25762 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
25763     
25764     getAutoCreate : function()
25765     {
25766         var cfg = {
25767             tag : 'div',
25768             cls : 'roo-document-viewer',
25769             cn : [
25770                 {
25771                     tag : 'div',
25772                     cls : 'roo-document-viewer-body',
25773                     cn : [
25774                         {
25775                             tag : 'div',
25776                             cls : 'roo-document-viewer-thumb',
25777                             cn : [
25778                                 {
25779                                     tag : 'img',
25780                                     cls : 'roo-document-viewer-image'
25781                                 }
25782                             ]
25783                         }
25784                     ]
25785                 },
25786                 {
25787                     tag : 'div',
25788                     cls : 'roo-document-viewer-footer',
25789                     cn : {
25790                         tag : 'div',
25791                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
25792                         cn : [
25793                             {
25794                                 tag : 'div',
25795                                 cls : 'btn-group',
25796                                 cn : [
25797                                     {
25798                                         tag : 'button',
25799                                         cls : 'btn btn-default roo-document-viewer-trash',
25800                                         html : '<i class="fa fa-trash"></i>'
25801                                     }
25802                                 ]
25803                             }
25804                         ]
25805                     }
25806                 }
25807             ]
25808         };
25809         
25810         return cfg;
25811     },
25812     
25813     initEvents : function()
25814     {
25815         
25816         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
25817         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25818         
25819         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
25820         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25821         
25822         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
25823         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25824         
25825         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
25826         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25827         
25828         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
25829         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25830         
25831         this.bodyEl.on('click', this.onClick, this);
25832         
25833         this.trashBtn.on('click', this.onTrash, this);
25834         
25835     },
25836     
25837     initial : function()
25838     {
25839 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
25840         
25841         
25842         this.fireEvent('initial', this);
25843         
25844     },
25845     
25846     onClick : function(e)
25847     {
25848         e.preventDefault();
25849         
25850         this.fireEvent('click', this);
25851     },
25852     
25853     onTrash : function(e)
25854     {
25855         e.preventDefault();
25856         
25857         this.fireEvent('trash', this);
25858     }
25859     
25860 });