76e6cdfcb7fdf3fae90078de7bb5148407690ab8
[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 {String} rheader contet on the right of header
993
994  *     
995  * @constructor
996  * Create a new Container
997  * @param {Object} config The config object
998  */
999
1000 Roo.bootstrap.Container = function(config){
1001     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1002     
1003     this.addEvents({
1004         // raw events
1005          /**
1006          * @event expand
1007          * After the panel has been expand
1008          * 
1009          * @param {Roo.bootstrap.Container} this
1010          */
1011         "expand" : true,
1012         /**
1013          * @event collapse
1014          * After the panel has been collapsed
1015          * 
1016          * @param {Roo.bootstrap.Container} this
1017          */
1018         "collapse" : true
1019     });
1020 };
1021
1022 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1023     
1024     jumbotron : false,
1025     well: '',
1026     panel : '',
1027     header: '',
1028     footer : '',
1029     sticky: '',
1030     tag : false,
1031     alert : false,
1032     fa: false,
1033     icon : false,
1034     expandable : false,
1035     rheader : '',
1036     expanded : true,
1037   
1038      
1039     getChildContainer : function() {
1040         
1041         if(!this.el){
1042             return false;
1043         }
1044         
1045         if (this.panel.length) {
1046             return this.el.select('.panel-body',true).first();
1047         }
1048         
1049         return this.el;
1050     },
1051     
1052     
1053     getAutoCreate : function(){
1054         
1055         var cfg = {
1056             tag : this.tag || 'div',
1057             html : '',
1058             cls : ''
1059         };
1060         if (this.jumbotron) {
1061             cfg.cls = 'jumbotron';
1062         }
1063         
1064         
1065         
1066         // - this is applied by the parent..
1067         //if (this.cls) {
1068         //    cfg.cls = this.cls + '';
1069         //}
1070         
1071         if (this.sticky.length) {
1072             
1073             var bd = Roo.get(document.body);
1074             if (!bd.hasClass('bootstrap-sticky')) {
1075                 bd.addClass('bootstrap-sticky');
1076                 Roo.select('html',true).setStyle('height', '100%');
1077             }
1078              
1079             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1080         }
1081         
1082         
1083         if (this.well.length) {
1084             switch (this.well) {
1085                 case 'lg':
1086                 case 'sm':
1087                     cfg.cls +=' well well-' +this.well;
1088                     break;
1089                 default:
1090                     cfg.cls +=' well';
1091                     break;
1092             }
1093         }
1094         
1095         if (this.hidden) {
1096             cfg.cls += ' hidden';
1097         }
1098         
1099         
1100         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1101             cfg.cls +=' alert alert-' + this.alert;
1102         }
1103         
1104         var body = cfg;
1105         
1106         if (this.panel.length) {
1107             cfg.cls += ' panel panel-' + this.panel;
1108             cfg.cn = [];
1109             if (this.header.length) {
1110                 
1111                 var h = [];
1112                 
1113                 if(this.expandable){
1114                     
1115                     cfg.cls = cfg.cls + ' expandable';
1116                     
1117                     h.push({
1118                         tag: 'i',
1119                         cls: 'fa fa-minus'
1120                     });
1121                 }
1122                 
1123                 h.push(
1124                     {
1125                         tag: 'span',
1126                         cls : 'panel-title',
1127                         html : this.header
1128                     },
1129                     {
1130                         tag: 'span',
1131                         cls: 'panel-header-right',
1132                         html: this.rheader
1133                     }
1134                 );
1135                 
1136                 cfg.cn.push({
1137                     cls : 'panel-heading',
1138                     cn : h
1139                 });
1140                 
1141             }
1142             
1143             body = false;
1144             cfg.cn.push({
1145                 cls : 'panel-body',
1146                 html : this.html
1147             });
1148             
1149             
1150             if (this.footer.length) {
1151                 cfg.cn.push({
1152                     cls : 'panel-footer',
1153                     html : this.footer
1154                     
1155                 });
1156             }
1157             
1158         }
1159         
1160         if (body) {
1161             body.html = this.html || cfg.html;
1162             // prefix with the icons..
1163             if (this.fa) {
1164                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1165             }
1166             if (this.icon) {
1167                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1168             }
1169             
1170             
1171         }
1172         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1173             cfg.cls =  'container';
1174         }
1175         
1176         return cfg;
1177     },
1178     
1179     initEvents: function() 
1180     {
1181         if(!this.expandable){
1182             return;
1183         }
1184         
1185         var headerEl = this.headerEl();
1186         
1187         if(!headerEl){
1188             return;
1189         }
1190         
1191         headerEl.on('click', this.onToggleClick, this);
1192         
1193     },
1194     
1195     onToggleClick : function()
1196     {
1197         var headerEl = this.headerEl();
1198         
1199         if(!headerEl){
1200             return;
1201         }
1202         
1203         if(this.expanded){
1204             this.collapse();
1205             return;
1206         }
1207         
1208         this.expand();
1209     },
1210     
1211     expand : function()
1212     {
1213         if(this.fireEvent('expand', this)) {
1214             
1215             this.expanded = true;
1216             
1217             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1218         
1219             var toggleEl = this.toggleEl();
1220
1221             if(!toggleEl){
1222                 return;
1223             }
1224
1225             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1226         }
1227         
1228     },
1229     
1230     collapse : function()
1231     {
1232         if(this.fireEvent('collapse', this)) {
1233             
1234             this.expanded = false;
1235             
1236             this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1237         
1238             var toggleEl = this.toggleEl();
1239
1240             if(!toggleEl){
1241                 return;
1242             }
1243
1244             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1245         }
1246     },
1247     
1248     toggleEl : function()
1249     {
1250         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1251             return;
1252         }
1253         
1254         return this.el.select('.panel-heading .fa',true).first();
1255     },
1256     
1257     headerEl : function()
1258     {
1259         if(!this.el || !this.panel.length || !this.header.length){
1260             return;
1261         }
1262         
1263         return this.el.select('.panel-heading',true).first()
1264     },
1265     
1266     titleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-title',true).first();
1273     },
1274     
1275     setTitle : function(v)
1276     {
1277         var titleEl = this.titleEl();
1278         
1279         if(!titleEl){
1280             return;
1281         }
1282         
1283         titleEl.dom.innerHTML = v;
1284     },
1285     
1286     getTitle : function()
1287     {
1288         
1289         var titleEl = this.titleEl();
1290         
1291         if(!titleEl){
1292             return '';
1293         }
1294         
1295         return titleEl.dom.innerHTML;
1296     },
1297     
1298     setRightTitle : function(v)
1299     {
1300         var t = this.el.select('.panel-header-right',true).first();
1301         
1302         if(!t){
1303             return;
1304         }
1305         
1306         t.dom.innerHTML = v;
1307     }
1308    
1309 });
1310
1311  /*
1312  * - LGPL
1313  *
1314  * image
1315  * 
1316  */
1317
1318
1319 /**
1320  * @class Roo.bootstrap.Img
1321  * @extends Roo.bootstrap.Component
1322  * Bootstrap Img class
1323  * @cfg {Boolean} imgResponsive false | true
1324  * @cfg {String} border rounded | circle | thumbnail
1325  * @cfg {String} src image source
1326  * @cfg {String} alt image alternative text
1327  * @cfg {String} href a tag href
1328  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1329  * @cfg {String} xsUrl xs image source
1330  * @cfg {String} smUrl sm image source
1331  * @cfg {String} mdUrl md image source
1332  * @cfg {String} lgUrl lg image source
1333  * 
1334  * @constructor
1335  * Create a new Input
1336  * @param {Object} config The config object
1337  */
1338
1339 Roo.bootstrap.Img = function(config){
1340     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1341     
1342     this.addEvents({
1343         // img events
1344         /**
1345          * @event click
1346          * The img click event for the img.
1347          * @param {Roo.EventObject} e
1348          */
1349         "click" : true
1350     });
1351 };
1352
1353 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1354     
1355     imgResponsive: true,
1356     border: '',
1357     src: '',
1358     href: false,
1359     target: false,
1360     xsUrl: '',
1361     smUrl: '',
1362     mdUrl: '',
1363     lgUrl: '',
1364
1365     getAutoCreate : function()
1366     {   
1367         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1368             return this.createSingleImg();
1369         }
1370         
1371         var cfg = {
1372             tag: 'div',
1373             cls: 'roo-image-responsive-group',
1374             cn: []
1375         }
1376         var _this = this;
1377         
1378         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1379             
1380             if(!_this[size + 'Url']){
1381                 return;
1382             }
1383             
1384             var img = {
1385                 tag: 'img',
1386                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1387                 html: _this.html || cfg.html,
1388                 src: _this[size + 'Url']
1389             }
1390             
1391             img.cls += ' roo-image-responsive-' + size;
1392             
1393             var s = ['xs', 'sm', 'md', 'lg'];
1394             
1395             s.splice(s.indexOf(size), 1);
1396             
1397             Roo.each(s, function(ss){
1398                 img.cls += ' hidden-' + ss;
1399             });
1400             
1401             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1402                 cfg.cls += ' img-' + _this.border;
1403             }
1404             
1405             if(_this.alt){
1406                 cfg.alt = _this.alt;
1407             }
1408             
1409             if(_this.href){
1410                 var a = {
1411                     tag: 'a',
1412                     href: _this.href,
1413                     cn: [
1414                         img
1415                     ]
1416                 }
1417
1418                 if(this.target){
1419                     a.target = _this.target;
1420                 }
1421             }
1422             
1423             cfg.cn.push((_this.href) ? a : img);
1424             
1425         });
1426         
1427         return cfg;
1428     },
1429     
1430     createSingleImg : function()
1431     {
1432         var cfg = {
1433             tag: 'img',
1434             cls: (this.imgResponsive) ? 'img-responsive' : '',
1435             html : null
1436         }
1437         
1438         cfg.html = this.html || cfg.html;
1439         
1440         cfg.src = this.src || cfg.src;
1441         
1442         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1443             cfg.cls += ' img-' + this.border;
1444         }
1445         
1446         if(this.alt){
1447             cfg.alt = this.alt;
1448         }
1449         
1450         if(this.href){
1451             var a = {
1452                 tag: 'a',
1453                 href: this.href,
1454                 cn: [
1455                     cfg
1456                 ]
1457             }
1458             
1459             if(this.target){
1460                 a.target = this.target;
1461             }
1462             
1463         }
1464         
1465         return (this.href) ? a : cfg;
1466     },
1467     
1468     initEvents: function() 
1469     {
1470         if(!this.href){
1471             this.el.on('click', this.onClick, this);
1472         }
1473         
1474     },
1475     
1476     onClick : function(e)
1477     {
1478         Roo.log('img onclick');
1479         this.fireEvent('click', this, e);
1480     }
1481    
1482 });
1483
1484  /*
1485  * - LGPL
1486  *
1487  * image
1488  * 
1489  */
1490
1491
1492 /**
1493  * @class Roo.bootstrap.Link
1494  * @extends Roo.bootstrap.Component
1495  * Bootstrap Link Class
1496  * @cfg {String} alt image alternative text
1497  * @cfg {String} href a tag href
1498  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1499  * @cfg {String} html the content of the link.
1500  * @cfg {String} anchor name for the anchor link
1501
1502  * @cfg {Boolean} preventDefault (true | false) default false
1503
1504  * 
1505  * @constructor
1506  * Create a new Input
1507  * @param {Object} config The config object
1508  */
1509
1510 Roo.bootstrap.Link = function(config){
1511     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1512     
1513     this.addEvents({
1514         // img events
1515         /**
1516          * @event click
1517          * The img click event for the img.
1518          * @param {Roo.EventObject} e
1519          */
1520         "click" : true
1521     });
1522 };
1523
1524 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1525     
1526     href: false,
1527     target: false,
1528     preventDefault: false,
1529     anchor : false,
1530     alt : false,
1531
1532     getAutoCreate : function()
1533     {
1534         
1535         var cfg = {
1536             tag: 'a'
1537         };
1538         // anchor's do not require html/href...
1539         if (this.anchor === false) {
1540             cfg.html = this.html || '';
1541             cfg.href = this.href || '#';
1542         } else {
1543             cfg.name = this.anchor;
1544             if (this.html !== false) {
1545                 cfg.html = this.html;
1546             }
1547             if (this.href !== false) {
1548                 cfg.href = this.href;
1549             }
1550         }
1551         
1552         if(this.alt !== false){
1553             cfg.alt = this.alt;
1554         }
1555         
1556         
1557         if(this.target !== false) {
1558             cfg.target = this.target;
1559         }
1560         
1561         return cfg;
1562     },
1563     
1564     initEvents: function() {
1565         
1566         if(!this.href || this.preventDefault){
1567             this.el.on('click', this.onClick, this);
1568         }
1569     },
1570     
1571     onClick : function(e)
1572     {
1573         if(this.preventDefault){
1574             e.preventDefault();
1575         }
1576         //Roo.log('img onclick');
1577         this.fireEvent('click', this, e);
1578     }
1579    
1580 });
1581
1582  /*
1583  * - LGPL
1584  *
1585  * header
1586  * 
1587  */
1588
1589 /**
1590  * @class Roo.bootstrap.Header
1591  * @extends Roo.bootstrap.Component
1592  * Bootstrap Header class
1593  * @cfg {String} html content of header
1594  * @cfg {Number} level (1|2|3|4|5|6) default 1
1595  * 
1596  * @constructor
1597  * Create a new Header
1598  * @param {Object} config The config object
1599  */
1600
1601
1602 Roo.bootstrap.Header  = function(config){
1603     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1604 };
1605
1606 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1607     
1608     //href : false,
1609     html : false,
1610     level : 1,
1611     
1612     
1613     
1614     getAutoCreate : function(){
1615         
1616         
1617         
1618         var cfg = {
1619             tag: 'h' + (1 *this.level),
1620             html: this.html || ''
1621         } ;
1622         
1623         return cfg;
1624     }
1625    
1626 });
1627
1628  
1629
1630  /*
1631  * Based on:
1632  * Ext JS Library 1.1.1
1633  * Copyright(c) 2006-2007, Ext JS, LLC.
1634  *
1635  * Originally Released Under LGPL - original licence link has changed is not relivant.
1636  *
1637  * Fork - LGPL
1638  * <script type="text/javascript">
1639  */
1640  
1641 /**
1642  * @class Roo.bootstrap.MenuMgr
1643  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1644  * @singleton
1645  */
1646 Roo.bootstrap.MenuMgr = function(){
1647    var menus, active, groups = {}, attached = false, lastShow = new Date();
1648
1649    // private - called when first menu is created
1650    function init(){
1651        menus = {};
1652        active = new Roo.util.MixedCollection();
1653        Roo.get(document).addKeyListener(27, function(){
1654            if(active.length > 0){
1655                hideAll();
1656            }
1657        });
1658    }
1659
1660    // private
1661    function hideAll(){
1662        if(active && active.length > 0){
1663            var c = active.clone();
1664            c.each(function(m){
1665                m.hide();
1666            });
1667        }
1668    }
1669
1670    // private
1671    function onHide(m){
1672        active.remove(m);
1673        if(active.length < 1){
1674            Roo.get(document).un("mouseup", onMouseDown);
1675             
1676            attached = false;
1677        }
1678    }
1679
1680    // private
1681    function onShow(m){
1682        var last = active.last();
1683        lastShow = new Date();
1684        active.add(m);
1685        if(!attached){
1686           Roo.get(document).on("mouseup", onMouseDown);
1687            
1688            attached = true;
1689        }
1690        if(m.parentMenu){
1691           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1692           m.parentMenu.activeChild = m;
1693        }else if(last && last.isVisible()){
1694           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1695        }
1696    }
1697
1698    // private
1699    function onBeforeHide(m){
1700        if(m.activeChild){
1701            m.activeChild.hide();
1702        }
1703        if(m.autoHideTimer){
1704            clearTimeout(m.autoHideTimer);
1705            delete m.autoHideTimer;
1706        }
1707    }
1708
1709    // private
1710    function onBeforeShow(m){
1711        var pm = m.parentMenu;
1712        if(!pm && !m.allowOtherMenus){
1713            hideAll();
1714        }else if(pm && pm.activeChild && active != m){
1715            pm.activeChild.hide();
1716        }
1717    }
1718
1719    // private this should really trigger on mouseup..
1720    function onMouseDown(e){
1721         Roo.log("on Mouse Up");
1722         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1723             Roo.log("hideAll");
1724             hideAll();
1725             e.stopEvent();
1726         }
1727         
1728         
1729    }
1730
1731    // private
1732    function onBeforeCheck(mi, state){
1733        if(state){
1734            var g = groups[mi.group];
1735            for(var i = 0, l = g.length; i < l; i++){
1736                if(g[i] != mi){
1737                    g[i].setChecked(false);
1738                }
1739            }
1740        }
1741    }
1742
1743    return {
1744
1745        /**
1746         * Hides all menus that are currently visible
1747         */
1748        hideAll : function(){
1749             hideAll();  
1750        },
1751
1752        // private
1753        register : function(menu){
1754            if(!menus){
1755                init();
1756            }
1757            menus[menu.id] = menu;
1758            menu.on("beforehide", onBeforeHide);
1759            menu.on("hide", onHide);
1760            menu.on("beforeshow", onBeforeShow);
1761            menu.on("show", onShow);
1762            var g = menu.group;
1763            if(g && menu.events["checkchange"]){
1764                if(!groups[g]){
1765                    groups[g] = [];
1766                }
1767                groups[g].push(menu);
1768                menu.on("checkchange", onCheck);
1769            }
1770        },
1771
1772         /**
1773          * Returns a {@link Roo.menu.Menu} object
1774          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1775          * be used to generate and return a new Menu instance.
1776          */
1777        get : function(menu){
1778            if(typeof menu == "string"){ // menu id
1779                return menus[menu];
1780            }else if(menu.events){  // menu instance
1781                return menu;
1782            }
1783            /*else if(typeof menu.length == 'number'){ // array of menu items?
1784                return new Roo.bootstrap.Menu({items:menu});
1785            }else{ // otherwise, must be a config
1786                return new Roo.bootstrap.Menu(menu);
1787            }
1788            */
1789            return false;
1790        },
1791
1792        // private
1793        unregister : function(menu){
1794            delete menus[menu.id];
1795            menu.un("beforehide", onBeforeHide);
1796            menu.un("hide", onHide);
1797            menu.un("beforeshow", onBeforeShow);
1798            menu.un("show", onShow);
1799            var g = menu.group;
1800            if(g && menu.events["checkchange"]){
1801                groups[g].remove(menu);
1802                menu.un("checkchange", onCheck);
1803            }
1804        },
1805
1806        // private
1807        registerCheckable : function(menuItem){
1808            var g = menuItem.group;
1809            if(g){
1810                if(!groups[g]){
1811                    groups[g] = [];
1812                }
1813                groups[g].push(menuItem);
1814                menuItem.on("beforecheckchange", onBeforeCheck);
1815            }
1816        },
1817
1818        // private
1819        unregisterCheckable : function(menuItem){
1820            var g = menuItem.group;
1821            if(g){
1822                groups[g].remove(menuItem);
1823                menuItem.un("beforecheckchange", onBeforeCheck);
1824            }
1825        }
1826    };
1827 }();/*
1828  * - LGPL
1829  *
1830  * menu
1831  * 
1832  */
1833
1834 /**
1835  * @class Roo.bootstrap.Menu
1836  * @extends Roo.bootstrap.Component
1837  * Bootstrap Menu class - container for MenuItems
1838  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1839  * 
1840  * @constructor
1841  * Create a new Menu
1842  * @param {Object} config The config object
1843  */
1844
1845
1846 Roo.bootstrap.Menu = function(config){
1847     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1848     if (this.registerMenu) {
1849         Roo.bootstrap.MenuMgr.register(this);
1850     }
1851     this.addEvents({
1852         /**
1853          * @event beforeshow
1854          * Fires before this menu is displayed
1855          * @param {Roo.menu.Menu} this
1856          */
1857         beforeshow : true,
1858         /**
1859          * @event beforehide
1860          * Fires before this menu is hidden
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforehide : true,
1864         /**
1865          * @event show
1866          * Fires after this menu is displayed
1867          * @param {Roo.menu.Menu} this
1868          */
1869         show : true,
1870         /**
1871          * @event hide
1872          * Fires after this menu is hidden
1873          * @param {Roo.menu.Menu} this
1874          */
1875         hide : true,
1876         /**
1877          * @event click
1878          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1879          * @param {Roo.menu.Menu} this
1880          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1881          * @param {Roo.EventObject} e
1882          */
1883         click : true,
1884         /**
1885          * @event mouseover
1886          * Fires when the mouse is hovering over this menu
1887          * @param {Roo.menu.Menu} this
1888          * @param {Roo.EventObject} e
1889          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1890          */
1891         mouseover : true,
1892         /**
1893          * @event mouseout
1894          * Fires when the mouse exits this menu
1895          * @param {Roo.menu.Menu} this
1896          * @param {Roo.EventObject} e
1897          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1898          */
1899         mouseout : true,
1900         /**
1901          * @event itemclick
1902          * Fires when a menu item contained in this menu is clicked
1903          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1904          * @param {Roo.EventObject} e
1905          */
1906         itemclick: true
1907     });
1908     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1909 };
1910
1911 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1912     
1913    /// html : false,
1914     //align : '',
1915     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1916     type: false,
1917     /**
1918      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1919      */
1920     registerMenu : true,
1921     
1922     menuItems :false, // stores the menu items..
1923     
1924     hidden:true,
1925     
1926     parentMenu : false,
1927     
1928     getChildContainer : function() {
1929         return this.el;  
1930     },
1931     
1932     getAutoCreate : function(){
1933          
1934         //if (['right'].indexOf(this.align)!==-1) {
1935         //    cfg.cn[1].cls += ' pull-right'
1936         //}
1937         
1938         
1939         var cfg = {
1940             tag : 'ul',
1941             cls : 'dropdown-menu' ,
1942             style : 'z-index:1000'
1943             
1944         }
1945         
1946         if (this.type === 'submenu') {
1947             cfg.cls = 'submenu active';
1948         }
1949         if (this.type === 'treeview') {
1950             cfg.cls = 'treeview-menu';
1951         }
1952         
1953         return cfg;
1954     },
1955     initEvents : function() {
1956         
1957        // Roo.log("ADD event");
1958        // Roo.log(this.triggerEl.dom);
1959         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1960         
1961         this.triggerEl.addClass('dropdown-toggle');
1962         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1963
1964         this.el.on("mouseover", this.onMouseOver, this);
1965         this.el.on("mouseout", this.onMouseOut, this);
1966         
1967         
1968     },
1969     findTargetItem : function(e){
1970         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1971         if(!t){
1972             return false;
1973         }
1974         //Roo.log(t);         Roo.log(t.id);
1975         if(t && t.id){
1976             //Roo.log(this.menuitems);
1977             return this.menuitems.get(t.id);
1978             
1979             //return this.items.get(t.menuItemId);
1980         }
1981         
1982         return false;
1983     },
1984     onClick : function(e){
1985         Roo.log("menu.onClick");
1986         var t = this.findTargetItem(e);
1987         if(!t || t.isContainer){
1988             return;
1989         }
1990         Roo.log(e);
1991         /*
1992         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1993             if(t == this.activeItem && t.shouldDeactivate(e)){
1994                 this.activeItem.deactivate();
1995                 delete this.activeItem;
1996                 return;
1997             }
1998             if(t.canActivate){
1999                 this.setActiveItem(t, true);
2000             }
2001             return;
2002             
2003             
2004         }
2005         */
2006        
2007         Roo.log('pass click event');
2008         
2009         t.onClick(e);
2010         
2011         this.fireEvent("click", this, t, e);
2012         
2013         this.hide();
2014     },
2015      onMouseOver : function(e){
2016         var t  = this.findTargetItem(e);
2017         //Roo.log(t);
2018         //if(t){
2019         //    if(t.canActivate && !t.disabled){
2020         //        this.setActiveItem(t, true);
2021         //    }
2022         //}
2023         
2024         this.fireEvent("mouseover", this, e, t);
2025     },
2026     isVisible : function(){
2027         return !this.hidden;
2028     },
2029      onMouseOut : function(e){
2030         var t  = this.findTargetItem(e);
2031         
2032         //if(t ){
2033         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2034         //        this.activeItem.deactivate();
2035         //        delete this.activeItem;
2036         //    }
2037         //}
2038         this.fireEvent("mouseout", this, e, t);
2039     },
2040     
2041     
2042     /**
2043      * Displays this menu relative to another element
2044      * @param {String/HTMLElement/Roo.Element} element The element to align to
2045      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2046      * the element (defaults to this.defaultAlign)
2047      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2048      */
2049     show : function(el, pos, parentMenu){
2050         this.parentMenu = parentMenu;
2051         if(!this.el){
2052             this.render();
2053         }
2054         this.fireEvent("beforeshow", this);
2055         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2056     },
2057      /**
2058      * Displays this menu at a specific xy position
2059      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2060      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2061      */
2062     showAt : function(xy, parentMenu, /* private: */_e){
2063         this.parentMenu = parentMenu;
2064         if(!this.el){
2065             this.render();
2066         }
2067         if(_e !== false){
2068             this.fireEvent("beforeshow", this);
2069             //xy = this.el.adjustForConstraints(xy);
2070         }
2071         
2072         //this.el.show();
2073         this.hideMenuItems();
2074         this.hidden = false;
2075         this.triggerEl.addClass('open');
2076         
2077         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2078             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2079         }
2080         
2081         this.el.setXY(xy);
2082         this.focus();
2083         this.fireEvent("show", this);
2084     },
2085     
2086     focus : function(){
2087         return;
2088         if(!this.hidden){
2089             this.doFocus.defer(50, this);
2090         }
2091     },
2092
2093     doFocus : function(){
2094         if(!this.hidden){
2095             this.focusEl.focus();
2096         }
2097     },
2098
2099     /**
2100      * Hides this menu and optionally all parent menus
2101      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2102      */
2103     hide : function(deep){
2104         
2105         this.hideMenuItems();
2106         if(this.el && this.isVisible()){
2107             this.fireEvent("beforehide", this);
2108             if(this.activeItem){
2109                 this.activeItem.deactivate();
2110                 this.activeItem = null;
2111             }
2112             this.triggerEl.removeClass('open');;
2113             this.hidden = true;
2114             this.fireEvent("hide", this);
2115         }
2116         if(deep === true && this.parentMenu){
2117             this.parentMenu.hide(true);
2118         }
2119     },
2120     
2121     onTriggerPress  : function(e)
2122     {
2123         
2124         Roo.log('trigger press');
2125         //Roo.log(e.getTarget());
2126        // Roo.log(this.triggerEl.dom);
2127         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2128             return;
2129         }
2130         
2131         if (this.isVisible()) {
2132             Roo.log('hide');
2133             this.hide();
2134         } else {
2135             Roo.log('show');
2136             this.show(this.triggerEl, false, false);
2137         }
2138         
2139         e.stopEvent();
2140     },
2141     
2142          
2143        
2144     
2145     hideMenuItems : function()
2146     {
2147         //$(backdrop).remove()
2148         Roo.select('.open',true).each(function(aa) {
2149             
2150             aa.removeClass('open');
2151           //var parent = getParent($(this))
2152           //var relatedTarget = { relatedTarget: this }
2153           
2154            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2155           //if (e.isDefaultPrevented()) return
2156            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2157         })
2158     },
2159     addxtypeChild : function (tree, cntr) {
2160         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2161           
2162         this.menuitems.add(comp);
2163         return comp;
2164
2165     },
2166     getEl : function()
2167     {
2168         Roo.log(this.el);
2169         return this.el;
2170     }
2171 });
2172
2173  
2174  /*
2175  * - LGPL
2176  *
2177  * menu item
2178  * 
2179  */
2180
2181
2182 /**
2183  * @class Roo.bootstrap.MenuItem
2184  * @extends Roo.bootstrap.Component
2185  * Bootstrap MenuItem class
2186  * @cfg {String} html the menu label
2187  * @cfg {String} href the link
2188  * @cfg {Boolean} preventDefault (true | false) default true
2189  * @cfg {Boolean} isContainer (true | false) default false
2190  * 
2191  * 
2192  * @constructor
2193  * Create a new MenuItem
2194  * @param {Object} config The config object
2195  */
2196
2197
2198 Roo.bootstrap.MenuItem = function(config){
2199     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2200     this.addEvents({
2201         // raw events
2202         /**
2203          * @event click
2204          * The raw click event for the entire grid.
2205          * @param {Roo.bootstrap.MenuItem} this
2206          * @param {Roo.EventObject} e
2207          */
2208         "click" : true
2209     });
2210 };
2211
2212 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2213     
2214     href : false,
2215     html : false,
2216     preventDefault: true,
2217     isContainer : false,
2218     
2219     getAutoCreate : function(){
2220         
2221         if(this.isContainer){
2222             return {
2223                 tag: 'li',
2224                 cls: 'dropdown-menu-item'
2225             };
2226         }
2227         
2228         var cfg= {
2229             tag: 'li',
2230             cls: 'dropdown-menu-item',
2231             cn: [
2232                     {
2233                         tag : 'a',
2234                         href : '#',
2235                         html : 'Link'
2236                     }
2237                 ]
2238         };
2239         if (this.parent().type == 'treeview') {
2240             cfg.cls = 'treeview-menu';
2241         }
2242         
2243         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2244         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2245         return cfg;
2246     },
2247     
2248     initEvents: function() {
2249         
2250         //this.el.select('a').on('click', this.onClick, this);
2251         
2252     },
2253     onClick : function(e)
2254     {
2255         Roo.log('item on click ');
2256         //if(this.preventDefault){
2257         //    e.preventDefault();
2258         //}
2259         //this.parent().hideMenuItems();
2260         
2261         this.fireEvent('click', this, e);
2262     },
2263     getEl : function()
2264     {
2265         return this.el;
2266     }
2267 });
2268
2269  
2270
2271  /*
2272  * - LGPL
2273  *
2274  * menu separator
2275  * 
2276  */
2277
2278
2279 /**
2280  * @class Roo.bootstrap.MenuSeparator
2281  * @extends Roo.bootstrap.Component
2282  * Bootstrap MenuSeparator class
2283  * 
2284  * @constructor
2285  * Create a new MenuItem
2286  * @param {Object} config The config object
2287  */
2288
2289
2290 Roo.bootstrap.MenuSeparator = function(config){
2291     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2292 };
2293
2294 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2295     
2296     getAutoCreate : function(){
2297         var cfg = {
2298             cls: 'divider',
2299             tag : 'li'
2300         };
2301         
2302         return cfg;
2303     }
2304    
2305 });
2306
2307  
2308
2309  
2310 /*
2311 * Licence: LGPL
2312 */
2313
2314 /**
2315  * @class Roo.bootstrap.Modal
2316  * @extends Roo.bootstrap.Component
2317  * Bootstrap Modal class
2318  * @cfg {String} title Title of dialog
2319  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2320  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2321  * @cfg {Boolean} specificTitle default false
2322  * @cfg {Array} buttons Array of buttons or standard button set..
2323  * @cfg {String} buttonPosition (left|right|center) default right
2324  * @cfg {Boolean} animate default true
2325  * @cfg {Boolean} allow_close default true
2326  * 
2327  * @constructor
2328  * Create a new Modal Dialog
2329  * @param {Object} config The config object
2330  */
2331
2332 Roo.bootstrap.Modal = function(config){
2333     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2334     this.addEvents({
2335         // raw events
2336         /**
2337          * @event btnclick
2338          * The raw btnclick event for the button
2339          * @param {Roo.EventObject} e
2340          */
2341         "btnclick" : true
2342     });
2343     this.buttons = this.buttons || [];
2344      
2345     if (this.tmpl) {
2346         this.tmpl = Roo.factory(this.tmpl);
2347     }
2348     
2349 };
2350
2351 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2352     
2353     title : 'test dialog',
2354    
2355     buttons : false,
2356     
2357     // set on load...
2358      
2359     html: false,
2360     
2361     tmp: false,
2362     
2363     specificTitle: false,
2364     
2365     buttonPosition: 'right',
2366     
2367     allow_close : true,
2368     
2369     animate : true,
2370     
2371     
2372      // private
2373     bodyEl:  false,
2374     footerEl:  false,
2375     titleEl:  false,
2376     closeEl:  false,
2377     
2378     
2379     onRender : function(ct, position)
2380     {
2381         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2382      
2383         if(!this.el){
2384             var cfg = Roo.apply({},  this.getAutoCreate());
2385             cfg.id = Roo.id();
2386             //if(!cfg.name){
2387             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2388             //}
2389             //if (!cfg.name.length) {
2390             //    delete cfg.name;
2391            // }
2392             if (this.cls) {
2393                 cfg.cls += ' ' + this.cls;
2394             }
2395             if (this.style) {
2396                 cfg.style = this.style;
2397             }
2398             this.el = Roo.get(document.body).createChild(cfg, position);
2399         }
2400         //var type = this.el.dom.type;
2401         
2402         
2403         
2404         
2405         if(this.tabIndex !== undefined){
2406             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2407         }
2408         
2409         
2410         this.bodyEl = this.el.select('.modal-body',true).first();
2411         this.closeEl = this.el.select('.modal-header .close', true).first();
2412         this.footerEl = this.el.select('.modal-footer',true).first();
2413         this.titleEl = this.el.select('.modal-title',true).first();
2414         
2415         
2416          
2417         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2418         this.maskEl.enableDisplayMode("block");
2419         this.maskEl.hide();
2420         //this.el.addClass("x-dlg-modal");
2421     
2422         if (this.buttons.length) {
2423             Roo.each(this.buttons, function(bb) {
2424                 b = Roo.apply({}, bb);
2425                 b.xns = b.xns || Roo.bootstrap;
2426                 b.xtype = b.xtype || 'Button';
2427                 if (typeof(b.listeners) == 'undefined') {
2428                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2429                 }
2430                 
2431                 var btn = Roo.factory(b);
2432                 
2433                 btn.onRender(this.el.select('.modal-footer div').first());
2434                 
2435             },this);
2436         }
2437         // render the children.
2438         var nitems = [];
2439         
2440         if(typeof(this.items) != 'undefined'){
2441             var items = this.items;
2442             delete this.items;
2443
2444             for(var i =0;i < items.length;i++) {
2445                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2446             }
2447         }
2448         
2449         this.items = nitems;
2450         
2451         // where are these used - they used to be body/close/footer
2452         
2453        
2454         this.initEvents();
2455         //this.el.addClass([this.fieldClass, this.cls]);
2456         
2457     },
2458     
2459     getAutoCreate : function(){
2460         
2461         
2462         var bdy = {
2463                 cls : 'modal-body',
2464                 html : this.html || ''
2465         };
2466         
2467         var title = {
2468             tag: 'h4',
2469             cls : 'modal-title',
2470             html : this.title
2471         };
2472         
2473         if(this.specificTitle){
2474             title = this.title;
2475             
2476         };
2477         
2478         var header = [];
2479         if (this.allow_close) {
2480             header.push({
2481                 tag: 'button',
2482                 cls : 'close',
2483                 html : '&times'
2484             });
2485         }
2486         header.push(title);
2487         
2488         var modal = {
2489             cls: "modal",
2490             style : 'display: none',
2491             cn : [
2492                 {
2493                     cls: "modal-dialog",
2494                     cn : [
2495                         {
2496                             cls : "modal-content",
2497                             cn : [
2498                                 {
2499                                     cls : 'modal-header',
2500                                     cn : header
2501                                 },
2502                                 bdy,
2503                                 {
2504                                     cls : 'modal-footer',
2505                                     cn : [
2506                                         {
2507                                             tag: 'div',
2508                                             cls: 'btn-' + this.buttonPosition
2509                                         }
2510                                     ]
2511                                     
2512                                 }
2513                                 
2514                                 
2515                             ]
2516                             
2517                         }
2518                     ]
2519                         
2520                 }
2521             ]
2522         };
2523         
2524         if(this.animate){
2525             modal.cls += ' fade';
2526         }
2527         
2528         return modal;
2529           
2530     },
2531     getChildContainer : function() {
2532          
2533          return this.bodyEl;
2534         
2535     },
2536     getButtonContainer : function() {
2537          return this.el.select('.modal-footer div',true).first();
2538         
2539     },
2540     initEvents : function()
2541     {
2542         if (this.allow_close) {
2543             this.closeEl.on('click', this.hide, this);
2544         }
2545
2546     },
2547     show : function() {
2548         
2549         if (!this.rendered) {
2550             this.render();
2551         }
2552         
2553         this.el.setStyle('display', 'block');
2554         
2555         if(this.animate){
2556             var _this = this;
2557             (function(){ _this.el.addClass('in'); }).defer(50);
2558         }else{
2559             this.el.addClass('in');
2560         }
2561         
2562         // not sure how we can show data in here.. 
2563         //if (this.tmpl) {
2564         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2565         //}
2566         
2567         Roo.get(document.body).addClass("x-body-masked");
2568         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2569         this.maskEl.show();
2570         this.el.setStyle('zIndex', '10001');
2571        
2572         this.fireEvent('show', this);
2573         
2574         
2575     },
2576     hide : function()
2577     {
2578         this.maskEl.hide();
2579         Roo.get(document.body).removeClass("x-body-masked");
2580         this.el.removeClass('in');
2581         
2582         if(this.animate){
2583             var _this = this;
2584             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2585         }else{
2586             this.el.setStyle('display', 'none');
2587         }
2588         
2589         this.fireEvent('hide', this);
2590     },
2591     
2592     addButton : function(str, cb)
2593     {
2594          
2595         
2596         var b = Roo.apply({}, { html : str } );
2597         b.xns = b.xns || Roo.bootstrap;
2598         b.xtype = b.xtype || 'Button';
2599         if (typeof(b.listeners) == 'undefined') {
2600             b.listeners = { click : cb.createDelegate(this)  };
2601         }
2602         
2603         var btn = Roo.factory(b);
2604            
2605         btn.onRender(this.el.select('.modal-footer div').first());
2606         
2607         return btn;   
2608        
2609     },
2610     
2611     setDefaultButton : function(btn)
2612     {
2613         //this.el.select('.modal-footer').()
2614     },
2615     resizeTo: function(w,h)
2616     {
2617         // skip..
2618     },
2619     setContentSize  : function(w, h)
2620     {
2621         
2622     },
2623     onButtonClick: function(btn,e)
2624     {
2625         //Roo.log([a,b,c]);
2626         this.fireEvent('btnclick', btn.name, e);
2627     },
2628      /**
2629      * Set the title of the Dialog
2630      * @param {String} str new Title
2631      */
2632     setTitle: function(str) {
2633         this.titleEl.dom.innerHTML = str;    
2634     },
2635     /**
2636      * Set the body of the Dialog
2637      * @param {String} str new Title
2638      */
2639     setBody: function(str) {
2640         this.bodyEl.dom.innerHTML = str;    
2641     },
2642     /**
2643      * Set the body of the Dialog using the template
2644      * @param {Obj} data - apply this data to the template and replace the body contents.
2645      */
2646     applyBody: function(obj)
2647     {
2648         if (!this.tmpl) {
2649             Roo.log("Error - using apply Body without a template");
2650             //code
2651         }
2652         this.tmpl.overwrite(this.bodyEl, obj);
2653     }
2654     
2655 });
2656
2657
2658 Roo.apply(Roo.bootstrap.Modal,  {
2659     /**
2660          * Button config that displays a single OK button
2661          * @type Object
2662          */
2663         OK :  [{
2664             name : 'ok',
2665             weight : 'primary',
2666             html : 'OK'
2667         }], 
2668         /**
2669          * Button config that displays Yes and No buttons
2670          * @type Object
2671          */
2672         YESNO : [
2673             {
2674                 name  : 'no',
2675                 html : 'No'
2676             },
2677             {
2678                 name  :'yes',
2679                 weight : 'primary',
2680                 html : 'Yes'
2681             }
2682         ],
2683         
2684         /**
2685          * Button config that displays OK and Cancel buttons
2686          * @type Object
2687          */
2688         OKCANCEL : [
2689             {
2690                name : 'cancel',
2691                 html : 'Cancel'
2692             },
2693             {
2694                 name : 'ok',
2695                 weight : 'primary',
2696                 html : 'OK'
2697             }
2698         ],
2699         /**
2700          * Button config that displays Yes, No and Cancel buttons
2701          * @type Object
2702          */
2703         YESNOCANCEL : [
2704             {
2705                 name : 'yes',
2706                 weight : 'primary',
2707                 html : 'Yes'
2708             },
2709             {
2710                 name : 'no',
2711                 html : 'No'
2712             },
2713             {
2714                 name : 'cancel',
2715                 html : 'Cancel'
2716             }
2717         ]
2718 });
2719  
2720  /*
2721  * - LGPL
2722  *
2723  * messagebox - can be used as a replace
2724  * 
2725  */
2726 /**
2727  * @class Roo.MessageBox
2728  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2729  * Example usage:
2730  *<pre><code>
2731 // Basic alert:
2732 Roo.Msg.alert('Status', 'Changes saved successfully.');
2733
2734 // Prompt for user data:
2735 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2736     if (btn == 'ok'){
2737         // process text value...
2738     }
2739 });
2740
2741 // Show a dialog using config options:
2742 Roo.Msg.show({
2743    title:'Save Changes?',
2744    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2745    buttons: Roo.Msg.YESNOCANCEL,
2746    fn: processResult,
2747    animEl: 'elId'
2748 });
2749 </code></pre>
2750  * @singleton
2751  */
2752 Roo.bootstrap.MessageBox = function(){
2753     var dlg, opt, mask, waitTimer;
2754     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2755     var buttons, activeTextEl, bwidth;
2756
2757     
2758     // private
2759     var handleButton = function(button){
2760         dlg.hide();
2761         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2762     };
2763
2764     // private
2765     var handleHide = function(){
2766         if(opt && opt.cls){
2767             dlg.el.removeClass(opt.cls);
2768         }
2769         //if(waitTimer){
2770         //    Roo.TaskMgr.stop(waitTimer);
2771         //    waitTimer = null;
2772         //}
2773     };
2774
2775     // private
2776     var updateButtons = function(b){
2777         var width = 0;
2778         if(!b){
2779             buttons["ok"].hide();
2780             buttons["cancel"].hide();
2781             buttons["yes"].hide();
2782             buttons["no"].hide();
2783             //dlg.footer.dom.style.display = 'none';
2784             return width;
2785         }
2786         dlg.footerEl.dom.style.display = '';
2787         for(var k in buttons){
2788             if(typeof buttons[k] != "function"){
2789                 if(b[k]){
2790                     buttons[k].show();
2791                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2792                     width += buttons[k].el.getWidth()+15;
2793                 }else{
2794                     buttons[k].hide();
2795                 }
2796             }
2797         }
2798         return width;
2799     };
2800
2801     // private
2802     var handleEsc = function(d, k, e){
2803         if(opt && opt.closable !== false){
2804             dlg.hide();
2805         }
2806         if(e){
2807             e.stopEvent();
2808         }
2809     };
2810
2811     return {
2812         /**
2813          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2814          * @return {Roo.BasicDialog} The BasicDialog element
2815          */
2816         getDialog : function(){
2817            if(!dlg){
2818                 dlg = new Roo.bootstrap.Modal( {
2819                     //draggable: true,
2820                     //resizable:false,
2821                     //constraintoviewport:false,
2822                     //fixedcenter:true,
2823                     //collapsible : false,
2824                     //shim:true,
2825                     //modal: true,
2826                   //  width:400,
2827                   //  height:100,
2828                     //buttonAlign:"center",
2829                     closeClick : function(){
2830                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2831                             handleButton("no");
2832                         }else{
2833                             handleButton("cancel");
2834                         }
2835                     }
2836                 });
2837                 dlg.render();
2838                 dlg.on("hide", handleHide);
2839                 mask = dlg.mask;
2840                 //dlg.addKeyListener(27, handleEsc);
2841                 buttons = {};
2842                 this.buttons = buttons;
2843                 var bt = this.buttonText;
2844                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2845                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2846                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2847                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2848                 Roo.log(buttons)
2849                 bodyEl = dlg.bodyEl.createChild({
2850
2851                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2852                         '<textarea class="roo-mb-textarea"></textarea>' +
2853                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2854                 });
2855                 msgEl = bodyEl.dom.firstChild;
2856                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2857                 textboxEl.enableDisplayMode();
2858                 textboxEl.addKeyListener([10,13], function(){
2859                     if(dlg.isVisible() && opt && opt.buttons){
2860                         if(opt.buttons.ok){
2861                             handleButton("ok");
2862                         }else if(opt.buttons.yes){
2863                             handleButton("yes");
2864                         }
2865                     }
2866                 });
2867                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2868                 textareaEl.enableDisplayMode();
2869                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2870                 progressEl.enableDisplayMode();
2871                 var pf = progressEl.dom.firstChild;
2872                 if (pf) {
2873                     pp = Roo.get(pf.firstChild);
2874                     pp.setHeight(pf.offsetHeight);
2875                 }
2876                 
2877             }
2878             return dlg;
2879         },
2880
2881         /**
2882          * Updates the message box body text
2883          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2884          * the XHTML-compliant non-breaking space character '&amp;#160;')
2885          * @return {Roo.MessageBox} This message box
2886          */
2887         updateText : function(text){
2888             if(!dlg.isVisible() && !opt.width){
2889                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2890             }
2891             msgEl.innerHTML = text || '&#160;';
2892       
2893             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2894             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2895             var w = Math.max(
2896                     Math.min(opt.width || cw , this.maxWidth), 
2897                     Math.max(opt.minWidth || this.minWidth, bwidth)
2898             );
2899             if(opt.prompt){
2900                 activeTextEl.setWidth(w);
2901             }
2902             if(dlg.isVisible()){
2903                 dlg.fixedcenter = false;
2904             }
2905             // to big, make it scroll. = But as usual stupid IE does not support
2906             // !important..
2907             
2908             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2909                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2910                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2911             } else {
2912                 bodyEl.dom.style.height = '';
2913                 bodyEl.dom.style.overflowY = '';
2914             }
2915             if (cw > w) {
2916                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2917             } else {
2918                 bodyEl.dom.style.overflowX = '';
2919             }
2920             
2921             dlg.setContentSize(w, bodyEl.getHeight());
2922             if(dlg.isVisible()){
2923                 dlg.fixedcenter = true;
2924             }
2925             return this;
2926         },
2927
2928         /**
2929          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2930          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2931          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2932          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2933          * @return {Roo.MessageBox} This message box
2934          */
2935         updateProgress : function(value, text){
2936             if(text){
2937                 this.updateText(text);
2938             }
2939             if (pp) { // weird bug on my firefox - for some reason this is not defined
2940                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2941             }
2942             return this;
2943         },        
2944
2945         /**
2946          * Returns true if the message box is currently displayed
2947          * @return {Boolean} True if the message box is visible, else false
2948          */
2949         isVisible : function(){
2950             return dlg && dlg.isVisible();  
2951         },
2952
2953         /**
2954          * Hides the message box if it is displayed
2955          */
2956         hide : function(){
2957             if(this.isVisible()){
2958                 dlg.hide();
2959             }  
2960         },
2961
2962         /**
2963          * Displays a new message box, or reinitializes an existing message box, based on the config options
2964          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2965          * The following config object properties are supported:
2966          * <pre>
2967 Property    Type             Description
2968 ----------  ---------------  ------------------------------------------------------------------------------------
2969 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2970                                    closes (defaults to undefined)
2971 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2972                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2973 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2974                                    progress and wait dialogs will ignore this property and always hide the
2975                                    close button as they can only be closed programmatically.
2976 cls               String           A custom CSS class to apply to the message box element
2977 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2978                                    displayed (defaults to 75)
2979 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2980                                    function will be btn (the name of the button that was clicked, if applicable,
2981                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2982                                    Progress and wait dialogs will ignore this option since they do not respond to
2983                                    user actions and can only be closed programmatically, so any required function
2984                                    should be called by the same code after it closes the dialog.
2985 icon              String           A CSS class that provides a background image to be used as an icon for
2986                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2987 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2988 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2989 modal             Boolean          False to allow user interaction with the page while the message box is
2990                                    displayed (defaults to true)
2991 msg               String           A string that will replace the existing message box body text (defaults
2992                                    to the XHTML-compliant non-breaking space character '&#160;')
2993 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2994 progress          Boolean          True to display a progress bar (defaults to false)
2995 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2996 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2997 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2998 title             String           The title text
2999 value             String           The string value to set into the active textbox element if displayed
3000 wait              Boolean          True to display a progress bar (defaults to false)
3001 width             Number           The width of the dialog in pixels
3002 </pre>
3003          *
3004          * Example usage:
3005          * <pre><code>
3006 Roo.Msg.show({
3007    title: 'Address',
3008    msg: 'Please enter your address:',
3009    width: 300,
3010    buttons: Roo.MessageBox.OKCANCEL,
3011    multiline: true,
3012    fn: saveAddress,
3013    animEl: 'addAddressBtn'
3014 });
3015 </code></pre>
3016          * @param {Object} config Configuration options
3017          * @return {Roo.MessageBox} This message box
3018          */
3019         show : function(options)
3020         {
3021             
3022             // this causes nightmares if you show one dialog after another
3023             // especially on callbacks..
3024              
3025             if(this.isVisible()){
3026                 
3027                 this.hide();
3028                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3029                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3030                 Roo.log("New Dialog Message:" +  options.msg )
3031                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3032                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3033                 
3034             }
3035             var d = this.getDialog();
3036             opt = options;
3037             d.setTitle(opt.title || "&#160;");
3038             d.closeEl.setDisplayed(opt.closable !== false);
3039             activeTextEl = textboxEl;
3040             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3041             if(opt.prompt){
3042                 if(opt.multiline){
3043                     textboxEl.hide();
3044                     textareaEl.show();
3045                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3046                         opt.multiline : this.defaultTextHeight);
3047                     activeTextEl = textareaEl;
3048                 }else{
3049                     textboxEl.show();
3050                     textareaEl.hide();
3051                 }
3052             }else{
3053                 textboxEl.hide();
3054                 textareaEl.hide();
3055             }
3056             progressEl.setDisplayed(opt.progress === true);
3057             this.updateProgress(0);
3058             activeTextEl.dom.value = opt.value || "";
3059             if(opt.prompt){
3060                 dlg.setDefaultButton(activeTextEl);
3061             }else{
3062                 var bs = opt.buttons;
3063                 var db = null;
3064                 if(bs && bs.ok){
3065                     db = buttons["ok"];
3066                 }else if(bs && bs.yes){
3067                     db = buttons["yes"];
3068                 }
3069                 dlg.setDefaultButton(db);
3070             }
3071             bwidth = updateButtons(opt.buttons);
3072             this.updateText(opt.msg);
3073             if(opt.cls){
3074                 d.el.addClass(opt.cls);
3075             }
3076             d.proxyDrag = opt.proxyDrag === true;
3077             d.modal = opt.modal !== false;
3078             d.mask = opt.modal !== false ? mask : false;
3079             if(!d.isVisible()){
3080                 // force it to the end of the z-index stack so it gets a cursor in FF
3081                 document.body.appendChild(dlg.el.dom);
3082                 d.animateTarget = null;
3083                 d.show(options.animEl);
3084             }
3085             return this;
3086         },
3087
3088         /**
3089          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3090          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3091          * and closing the message box when the process is complete.
3092          * @param {String} title The title bar text
3093          * @param {String} msg The message box body text
3094          * @return {Roo.MessageBox} This message box
3095          */
3096         progress : function(title, msg){
3097             this.show({
3098                 title : title,
3099                 msg : msg,
3100                 buttons: false,
3101                 progress:true,
3102                 closable:false,
3103                 minWidth: this.minProgressWidth,
3104                 modal : true
3105             });
3106             return this;
3107         },
3108
3109         /**
3110          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3111          * If a callback function is passed it will be called after the user clicks the button, and the
3112          * id of the button that was clicked will be passed as the only parameter to the callback
3113          * (could also be the top-right close button).
3114          * @param {String} title The title bar text
3115          * @param {String} msg The message box body text
3116          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3117          * @param {Object} scope (optional) The scope of the callback function
3118          * @return {Roo.MessageBox} This message box
3119          */
3120         alert : function(title, msg, fn, scope){
3121             this.show({
3122                 title : title,
3123                 msg : msg,
3124                 buttons: this.OK,
3125                 fn: fn,
3126                 scope : scope,
3127                 modal : true
3128             });
3129             return this;
3130         },
3131
3132         /**
3133          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3134          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3135          * You are responsible for closing the message box when the process is complete.
3136          * @param {String} msg The message box body text
3137          * @param {String} title (optional) The title bar text
3138          * @return {Roo.MessageBox} This message box
3139          */
3140         wait : function(msg, title){
3141             this.show({
3142                 title : title,
3143                 msg : msg,
3144                 buttons: false,
3145                 closable:false,
3146                 progress:true,
3147                 modal:true,
3148                 width:300,
3149                 wait:true
3150             });
3151             waitTimer = Roo.TaskMgr.start({
3152                 run: function(i){
3153                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3154                 },
3155                 interval: 1000
3156             });
3157             return this;
3158         },
3159
3160         /**
3161          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3162          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3163          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3164          * @param {String} title The title bar text
3165          * @param {String} msg The message box body text
3166          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3167          * @param {Object} scope (optional) The scope of the callback function
3168          * @return {Roo.MessageBox} This message box
3169          */
3170         confirm : function(title, msg, fn, scope){
3171             this.show({
3172                 title : title,
3173                 msg : msg,
3174                 buttons: this.YESNO,
3175                 fn: fn,
3176                 scope : scope,
3177                 modal : true
3178             });
3179             return this;
3180         },
3181
3182         /**
3183          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3184          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3185          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3186          * (could also be the top-right close button) and the text that was entered will be passed as the two
3187          * parameters to the callback.
3188          * @param {String} title The title bar text
3189          * @param {String} msg The message box body text
3190          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3191          * @param {Object} scope (optional) The scope of the callback function
3192          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3193          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3194          * @return {Roo.MessageBox} This message box
3195          */
3196         prompt : function(title, msg, fn, scope, multiline){
3197             this.show({
3198                 title : title,
3199                 msg : msg,
3200                 buttons: this.OKCANCEL,
3201                 fn: fn,
3202                 minWidth:250,
3203                 scope : scope,
3204                 prompt:true,
3205                 multiline: multiline,
3206                 modal : true
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Button config that displays a single OK button
3213          * @type Object
3214          */
3215         OK : {ok:true},
3216         /**
3217          * Button config that displays Yes and No buttons
3218          * @type Object
3219          */
3220         YESNO : {yes:true, no:true},
3221         /**
3222          * Button config that displays OK and Cancel buttons
3223          * @type Object
3224          */
3225         OKCANCEL : {ok:true, cancel:true},
3226         /**
3227          * Button config that displays Yes, No and Cancel buttons
3228          * @type Object
3229          */
3230         YESNOCANCEL : {yes:true, no:true, cancel:true},
3231
3232         /**
3233          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3234          * @type Number
3235          */
3236         defaultTextHeight : 75,
3237         /**
3238          * The maximum width in pixels of the message box (defaults to 600)
3239          * @type Number
3240          */
3241         maxWidth : 600,
3242         /**
3243          * The minimum width in pixels of the message box (defaults to 100)
3244          * @type Number
3245          */
3246         minWidth : 100,
3247         /**
3248          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3249          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3250          * @type Number
3251          */
3252         minProgressWidth : 250,
3253         /**
3254          * An object containing the default button text strings that can be overriden for localized language support.
3255          * Supported properties are: ok, cancel, yes and no.
3256          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3257          * @type Object
3258          */
3259         buttonText : {
3260             ok : "OK",
3261             cancel : "Cancel",
3262             yes : "Yes",
3263             no : "No"
3264         }
3265     };
3266 }();
3267
3268 /**
3269  * Shorthand for {@link Roo.MessageBox}
3270  */
3271 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3272 Roo.Msg = Roo.Msg || Roo.MessageBox;
3273 /*
3274  * - LGPL
3275  *
3276  * navbar
3277  * 
3278  */
3279
3280 /**
3281  * @class Roo.bootstrap.Navbar
3282  * @extends Roo.bootstrap.Component
3283  * Bootstrap Navbar class
3284
3285  * @constructor
3286  * Create a new Navbar
3287  * @param {Object} config The config object
3288  */
3289
3290
3291 Roo.bootstrap.Navbar = function(config){
3292     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3293     
3294 };
3295
3296 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3297     
3298     
3299    
3300     // private
3301     navItems : false,
3302     loadMask : false,
3303     
3304     
3305     getAutoCreate : function(){
3306         
3307         
3308         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3309         
3310     },
3311     
3312     initEvents :function ()
3313     {
3314         //Roo.log(this.el.select('.navbar-toggle',true));
3315         this.el.select('.navbar-toggle',true).on('click', function() {
3316            // Roo.log('click');
3317             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3318         }, this);
3319         
3320         var mark = {
3321             tag: "div",
3322             cls:"x-dlg-mask"
3323         }
3324         
3325         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3326         
3327         var size = this.el.getSize();
3328         this.maskEl.setSize(size.width, size.height);
3329         this.maskEl.enableDisplayMode("block");
3330         this.maskEl.hide();
3331         
3332         if(this.loadMask){
3333             this.maskEl.show();
3334         }
3335     },
3336     
3337     
3338     getChildContainer : function()
3339     {
3340         if (this.el.select('.collapse').getCount()) {
3341             return this.el.select('.collapse',true).first();
3342         }
3343         
3344         return this.el;
3345     },
3346     
3347     mask : function()
3348     {
3349         this.maskEl.show();
3350     },
3351     
3352     unmask : function()
3353     {
3354         this.maskEl.hide();
3355     } 
3356     
3357     
3358     
3359     
3360 });
3361
3362
3363
3364  
3365
3366  /*
3367  * - LGPL
3368  *
3369  * navbar
3370  * 
3371  */
3372
3373 /**
3374  * @class Roo.bootstrap.NavSimplebar
3375  * @extends Roo.bootstrap.Navbar
3376  * Bootstrap Sidebar class
3377  *
3378  * @cfg {Boolean} inverse is inverted color
3379  * 
3380  * @cfg {String} type (nav | pills | tabs)
3381  * @cfg {Boolean} arrangement stacked | justified
3382  * @cfg {String} align (left | right) alignment
3383  * 
3384  * @cfg {Boolean} main (true|false) main nav bar? default false
3385  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3386  * 
3387  * @cfg {String} tag (header|footer|nav|div) default is nav 
3388
3389  * 
3390  * 
3391  * 
3392  * @constructor
3393  * Create a new Sidebar
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.NavSimplebar = function(config){
3399     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3403     
3404     inverse: false,
3405     
3406     type: false,
3407     arrangement: '',
3408     align : false,
3409     
3410     
3411     
3412     main : false,
3413     
3414     
3415     tag : false,
3416     
3417     
3418     getAutoCreate : function(){
3419         
3420         
3421         var cfg = {
3422             tag : this.tag || 'div',
3423             cls : 'navbar'
3424         };
3425           
3426         
3427         cfg.cn = [
3428             {
3429                 cls: 'nav',
3430                 tag : 'ul'
3431             }
3432         ];
3433         
3434          
3435         this.type = this.type || 'nav';
3436         if (['tabs','pills'].indexOf(this.type)!==-1) {
3437             cfg.cn[0].cls += ' nav-' + this.type
3438         
3439         
3440         } else {
3441             if (this.type!=='nav') {
3442                 Roo.log('nav type must be nav/tabs/pills')
3443             }
3444             cfg.cn[0].cls += ' navbar-nav'
3445         }
3446         
3447         
3448         
3449         
3450         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3451             cfg.cn[0].cls += ' nav-' + this.arrangement;
3452         }
3453         
3454         
3455         if (this.align === 'right') {
3456             cfg.cn[0].cls += ' navbar-right';
3457         }
3458         
3459         if (this.inverse) {
3460             cfg.cls += ' navbar-inverse';
3461             
3462         }
3463         
3464         
3465         return cfg;
3466     
3467         
3468     }
3469     
3470     
3471     
3472 });
3473
3474
3475
3476  
3477
3478  
3479        /*
3480  * - LGPL
3481  *
3482  * navbar
3483  * 
3484  */
3485
3486 /**
3487  * @class Roo.bootstrap.NavHeaderbar
3488  * @extends Roo.bootstrap.NavSimplebar
3489  * Bootstrap Sidebar class
3490  *
3491  * @cfg {String} brand what is brand
3492  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3493  * @cfg {String} brand_href href of the brand
3494  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3495  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3496  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3497  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3498  * 
3499  * @constructor
3500  * Create a new Sidebar
3501  * @param {Object} config The config object
3502  */
3503
3504
3505 Roo.bootstrap.NavHeaderbar = function(config){
3506     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3507       
3508 };
3509
3510 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3511     
3512     position: '',
3513     brand: '',
3514     brand_href: false,
3515     srButton : true,
3516     autohide : false,
3517     desktopCenter : false,
3518    
3519     
3520     getAutoCreate : function(){
3521         
3522         var   cfg = {
3523             tag: this.nav || 'nav',
3524             cls: 'navbar',
3525             role: 'navigation',
3526             cn: []
3527         };
3528         
3529         var cn = cfg.cn;
3530         if (this.desktopCenter) {
3531             cn.push({cls : 'container', cn : []});
3532             cn = cn[0].cn;
3533         }
3534         
3535         if(this.srButton){
3536             cn.push({
3537                 tag: 'div',
3538                 cls: 'navbar-header',
3539                 cn: [
3540                     {
3541                         tag: 'button',
3542                         type: 'button',
3543                         cls: 'navbar-toggle',
3544                         'data-toggle': 'collapse',
3545                         cn: [
3546                             {
3547                                 tag: 'span',
3548                                 cls: 'sr-only',
3549                                 html: 'Toggle navigation'
3550                             },
3551                             {
3552                                 tag: 'span',
3553                                 cls: 'icon-bar'
3554                             },
3555                             {
3556                                 tag: 'span',
3557                                 cls: 'icon-bar'
3558                             },
3559                             {
3560                                 tag: 'span',
3561                                 cls: 'icon-bar'
3562                             }
3563                         ]
3564                     }
3565                 ]
3566             });
3567         }
3568         
3569         cn.push({
3570             tag: 'div',
3571             cls: 'collapse navbar-collapse',
3572             cn : []
3573         });
3574         
3575         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3576         
3577         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3578             cfg.cls += ' navbar-' + this.position;
3579             
3580             // tag can override this..
3581             
3582             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3583         }
3584         
3585         if (this.brand !== '') {
3586             cn[0].cn.push({
3587                 tag: 'a',
3588                 href: this.brand_href ? this.brand_href : '#',
3589                 cls: 'navbar-brand',
3590                 cn: [
3591                 this.brand
3592                 ]
3593             });
3594         }
3595         
3596         if(this.main){
3597             cfg.cls += ' main-nav';
3598         }
3599         
3600         
3601         return cfg;
3602
3603         
3604     },
3605     getHeaderChildContainer : function()
3606     {
3607         if (this.el.select('.navbar-header').getCount()) {
3608             return this.el.select('.navbar-header',true).first();
3609         }
3610         
3611         return this.getChildContainer();
3612     },
3613     
3614     
3615     initEvents : function()
3616     {
3617         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3618         
3619         if (this.autohide) {
3620             
3621             var prevScroll = 0;
3622             var ft = this.el;
3623             
3624             Roo.get(document).on('scroll',function(e) {
3625                 var ns = Roo.get(document).getScroll().top;
3626                 var os = prevScroll;
3627                 prevScroll = ns;
3628                 
3629                 if(ns > os){
3630                     ft.removeClass('slideDown');
3631                     ft.addClass('slideUp');
3632                     return;
3633                 }
3634                 ft.removeClass('slideUp');
3635                 ft.addClass('slideDown');
3636                  
3637               
3638           },this);
3639         }
3640     }    
3641     
3642 });
3643
3644
3645
3646  
3647
3648  /*
3649  * - LGPL
3650  *
3651  * navbar
3652  * 
3653  */
3654
3655 /**
3656  * @class Roo.bootstrap.NavSidebar
3657  * @extends Roo.bootstrap.Navbar
3658  * Bootstrap Sidebar class
3659  * 
3660  * @constructor
3661  * Create a new Sidebar
3662  * @param {Object} config The config object
3663  */
3664
3665
3666 Roo.bootstrap.NavSidebar = function(config){
3667     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3668 };
3669
3670 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3671     
3672     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3673     
3674     getAutoCreate : function(){
3675         
3676         
3677         return  {
3678             tag: 'div',
3679             cls: 'sidebar sidebar-nav'
3680         };
3681     
3682         
3683     }
3684     
3685     
3686     
3687 });
3688
3689
3690
3691  
3692
3693  /*
3694  * - LGPL
3695  *
3696  * nav group
3697  * 
3698  */
3699
3700 /**
3701  * @class Roo.bootstrap.NavGroup
3702  * @extends Roo.bootstrap.Component
3703  * Bootstrap NavGroup class
3704  * @cfg {String} align (left|right)
3705  * @cfg {Boolean} inverse
3706  * @cfg {String} type (nav|pills|tab) default nav
3707  * @cfg {String} navId - reference Id for navbar.
3708
3709  * 
3710  * @constructor
3711  * Create a new nav group
3712  * @param {Object} config The config object
3713  */
3714
3715 Roo.bootstrap.NavGroup = function(config){
3716     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3717     this.navItems = [];
3718    
3719     Roo.bootstrap.NavGroup.register(this);
3720      this.addEvents({
3721         /**
3722              * @event changed
3723              * Fires when the active item changes
3724              * @param {Roo.bootstrap.NavGroup} this
3725              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3726              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3727          */
3728         'changed': true
3729      });
3730     
3731 };
3732
3733 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3734     
3735     align: '',
3736     inverse: false,
3737     form: false,
3738     type: 'nav',
3739     navId : '',
3740     // private
3741     
3742     navItems : false, 
3743     
3744     getAutoCreate : function()
3745     {
3746         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3747         
3748         cfg = {
3749             tag : 'ul',
3750             cls: 'nav' 
3751         }
3752         
3753         if (['tabs','pills'].indexOf(this.type)!==-1) {
3754             cfg.cls += ' nav-' + this.type
3755         } else {
3756             if (this.type!=='nav') {
3757                 Roo.log('nav type must be nav/tabs/pills')
3758             }
3759             cfg.cls += ' navbar-nav'
3760         }
3761         
3762         if (this.parent().sidebar) {
3763             cfg = {
3764                 tag: 'ul',
3765                 cls: 'dashboard-menu sidebar-menu'
3766             }
3767             
3768             return cfg;
3769         }
3770         
3771         if (this.form === true) {
3772             cfg = {
3773                 tag: 'form',
3774                 cls: 'navbar-form'
3775             }
3776             
3777             if (this.align === 'right') {
3778                 cfg.cls += ' navbar-right';
3779             } else {
3780                 cfg.cls += ' navbar-left';
3781             }
3782         }
3783         
3784         if (this.align === 'right') {
3785             cfg.cls += ' navbar-right';
3786         }
3787         
3788         if (this.inverse) {
3789             cfg.cls += ' navbar-inverse';
3790             
3791         }
3792         
3793         
3794         return cfg;
3795     },
3796     /**
3797     * sets the active Navigation item
3798     * @param {Roo.bootstrap.NavItem} the new current navitem
3799     */
3800     setActiveItem : function(item)
3801     {
3802         var prev = false;
3803         Roo.each(this.navItems, function(v){
3804             if (v == item) {
3805                 return ;
3806             }
3807             if (v.isActive()) {
3808                 v.setActive(false, true);
3809                 prev = v;
3810                 
3811             }
3812             
3813         });
3814
3815         item.setActive(true, true);
3816         this.fireEvent('changed', this, item, prev);
3817         
3818         
3819     },
3820     /**
3821     * gets the active Navigation item
3822     * @return {Roo.bootstrap.NavItem} the current navitem
3823     */
3824     getActive : function()
3825     {
3826         
3827         var prev = false;
3828         Roo.each(this.navItems, function(v){
3829             
3830             if (v.isActive()) {
3831                 prev = v;
3832                 
3833             }
3834             
3835         });
3836         return prev;
3837     },
3838     
3839     indexOfNav : function()
3840     {
3841         
3842         var prev = false;
3843         Roo.each(this.navItems, function(v,i){
3844             
3845             if (v.isActive()) {
3846                 prev = i;
3847                 
3848             }
3849             
3850         });
3851         return prev;
3852     },
3853     /**
3854     * adds a Navigation item
3855     * @param {Roo.bootstrap.NavItem} the navitem to add
3856     */
3857     addItem : function(cfg)
3858     {
3859         var cn = new Roo.bootstrap.NavItem(cfg);
3860         this.register(cn);
3861         cn.parentId = this.id;
3862         cn.onRender(this.el, null);
3863         return cn;
3864     },
3865     /**
3866     * register a Navigation item
3867     * @param {Roo.bootstrap.NavItem} the navitem to add
3868     */
3869     register : function(item)
3870     {
3871         this.navItems.push( item);
3872         item.navId = this.navId;
3873     
3874     },
3875     
3876     /**
3877     * clear all the Navigation item
3878     */
3879    
3880     clearAll : function()
3881     {
3882         this.navItems = [];
3883         this.el.dom.innerHTML = '';
3884     },
3885     
3886     getNavItem: function(tabId)
3887     {
3888         var ret = false;
3889         Roo.each(this.navItems, function(e) {
3890             if (e.tabId == tabId) {
3891                ret =  e;
3892                return false;
3893             }
3894             return true;
3895             
3896         });
3897         return ret;
3898     },
3899     
3900     setActiveNext : function()
3901     {
3902         var i = this.indexOfNav(this.getActive());
3903         if (i > this.navItems.length) {
3904             return;
3905         }
3906         this.setActiveItem(this.navItems[i+1]);
3907     },
3908     setActivePrev : function()
3909     {
3910         var i = this.indexOfNav(this.getActive());
3911         if (i  < 1) {
3912             return;
3913         }
3914         this.setActiveItem(this.navItems[i-1]);
3915     },
3916     clearWasActive : function(except) {
3917         Roo.each(this.navItems, function(e) {
3918             if (e.tabId != except.tabId && e.was_active) {
3919                e.was_active = false;
3920                return false;
3921             }
3922             return true;
3923             
3924         });
3925     },
3926     getWasActive : function ()
3927     {
3928         var r = false;
3929         Roo.each(this.navItems, function(e) {
3930             if (e.was_active) {
3931                r = e;
3932                return false;
3933             }
3934             return true;
3935             
3936         });
3937         return r;
3938     }
3939     
3940     
3941 });
3942
3943  
3944 Roo.apply(Roo.bootstrap.NavGroup, {
3945     
3946     groups: {},
3947      /**
3948     * register a Navigation Group
3949     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3950     */
3951     register : function(navgrp)
3952     {
3953         this.groups[navgrp.navId] = navgrp;
3954         
3955     },
3956     /**
3957     * fetch a Navigation Group based on the navigation ID
3958     * @param {string} the navgroup to add
3959     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3960     */
3961     get: function(navId) {
3962         if (typeof(this.groups[navId]) == 'undefined') {
3963             return false;
3964             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3965         }
3966         return this.groups[navId] ;
3967     }
3968     
3969     
3970     
3971 });
3972
3973  /*
3974  * - LGPL
3975  *
3976  * row
3977  * 
3978  */
3979
3980 /**
3981  * @class Roo.bootstrap.NavItem
3982  * @extends Roo.bootstrap.Component
3983  * Bootstrap Navbar.NavItem class
3984  * @cfg {String} href  link to
3985  * @cfg {String} html content of button
3986  * @cfg {String} badge text inside badge
3987  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3988  * @cfg {String} glyphicon name of glyphicon
3989  * @cfg {String} icon name of font awesome icon
3990  * @cfg {Boolean} active Is item active
3991  * @cfg {Boolean} disabled Is item disabled
3992  
3993  * @cfg {Boolean} preventDefault (true | false) default false
3994  * @cfg {String} tabId the tab that this item activates.
3995  * @cfg {String} tagtype (a|span) render as a href or span?
3996  * @cfg {Boolean} animateRef (true|false) link to element default false  
3997   
3998  * @constructor
3999  * Create a new Navbar Item
4000  * @param {Object} config The config object
4001  */
4002 Roo.bootstrap.NavItem = function(config){
4003     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4004     this.addEvents({
4005         // raw events
4006         /**
4007          * @event click
4008          * The raw click event for the entire grid.
4009          * @param {Roo.EventObject} e
4010          */
4011         "click" : true,
4012          /**
4013             * @event changed
4014             * Fires when the active item active state changes
4015             * @param {Roo.bootstrap.NavItem} this
4016             * @param {boolean} state the new state
4017              
4018          */
4019         'changed': true,
4020         /**
4021             * @event scrollto
4022             * Fires when scroll to element
4023             * @param {Roo.bootstrap.NavItem} this
4024             * @param {Object} options
4025             * @param {Roo.EventObject} e
4026              
4027          */
4028         'scrollto': true
4029     });
4030    
4031 };
4032
4033 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4034     
4035     href: false,
4036     html: '',
4037     badge: '',
4038     icon: false,
4039     glyphicon: false,
4040     active: false,
4041     preventDefault : false,
4042     tabId : false,
4043     tagtype : 'a',
4044     disabled : false,
4045     animateRef : false,
4046     was_active : false,
4047     
4048     getAutoCreate : function(){
4049          
4050         var cfg = {
4051             tag: 'li',
4052             cls: 'nav-item'
4053             
4054         }
4055         if (this.active) {
4056             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4057         }
4058         if (this.disabled) {
4059             cfg.cls += ' disabled';
4060         }
4061         
4062         if (this.href || this.html || this.glyphicon || this.icon) {
4063             cfg.cn = [
4064                 {
4065                     tag: this.tagtype,
4066                     href : this.href || "#",
4067                     html: this.html || ''
4068                 }
4069             ];
4070             
4071             if (this.icon) {
4072                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4073             }
4074
4075             if(this.glyphicon) {
4076                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4077             }
4078             
4079             if (this.menu) {
4080                 
4081                 cfg.cn[0].html += " <span class='caret'></span>";
4082              
4083             }
4084             
4085             if (this.badge !== '') {
4086                  
4087                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4088             }
4089         }
4090         
4091         
4092         
4093         return cfg;
4094     },
4095     initEvents: function() 
4096     {
4097         if (typeof (this.menu) != 'undefined') {
4098             this.menu.parentType = this.xtype;
4099             this.menu.triggerEl = this.el;
4100             this.menu = this.addxtype(Roo.apply({}, this.menu));
4101         }
4102         
4103         this.el.select('a',true).on('click', this.onClick, this);
4104         
4105         if(this.tagtype == 'span'){
4106             this.el.select('span',true).on('click', this.onClick, this);
4107         }
4108        
4109         // at this point parent should be available..
4110         this.parent().register(this);
4111     },
4112     
4113     onClick : function(e)
4114     {
4115         if(
4116                 this.preventDefault || 
4117                 this.href == '#' 
4118         ){
4119             
4120             e.preventDefault();
4121         }
4122         
4123         if (this.disabled) {
4124             return;
4125         }
4126         
4127         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4128         if (tg && tg.transition) {
4129             Roo.log("waiting for the transitionend");
4130             return;
4131         }
4132         
4133         
4134         
4135         //Roo.log("fire event clicked");
4136         if(this.fireEvent('click', this, e) === false){
4137             return;
4138         };
4139         
4140         if(this.tagtype == 'span'){
4141             return;
4142         }
4143         
4144         //Roo.log(this.href);
4145         var ael = this.el.select('a',true).first();
4146         //Roo.log(ael);
4147         
4148         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4149             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4150             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4151                 return; // ignore... - it's a 'hash' to another page.
4152             }
4153             
4154             e.preventDefault();
4155             this.scrollToElement(e);
4156         }
4157         
4158         
4159         var p =  this.parent();
4160    
4161         if (['tabs','pills'].indexOf(p.type)!==-1) {
4162             if (typeof(p.setActiveItem) !== 'undefined') {
4163                 p.setActiveItem(this);
4164             }
4165         }
4166         
4167         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4168         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4169             // remove the collapsed menu expand...
4170             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4171         }
4172     },
4173     
4174     isActive: function () {
4175         return this.active
4176     },
4177     setActive : function(state, fire, is_was_active)
4178     {
4179         if (this.active && !state & this.navId) {
4180             this.was_active = true;
4181             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4182             if (nv) {
4183                 nv.clearWasActive(this);
4184             }
4185             
4186         }
4187         this.active = state;
4188         
4189         if (!state ) {
4190             this.el.removeClass('active');
4191         } else if (!this.el.hasClass('active')) {
4192             this.el.addClass('active');
4193         }
4194         if (fire) {
4195             this.fireEvent('changed', this, state);
4196         }
4197         
4198         // show a panel if it's registered and related..
4199         
4200         if (!this.navId || !this.tabId || !state || is_was_active) {
4201             return;
4202         }
4203         
4204         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4205         if (!tg) {
4206             return;
4207         }
4208         var pan = tg.getPanelByName(this.tabId);
4209         if (!pan) {
4210             return;
4211         }
4212         // if we can not flip to new panel - go back to old nav highlight..
4213         if (false == tg.showPanel(pan)) {
4214             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4215             if (nv) {
4216                 var onav = nv.getWasActive();
4217                 if (onav) {
4218                     onav.setActive(true, false, true);
4219                 }
4220             }
4221             
4222         }
4223         
4224         
4225         
4226     },
4227      // this should not be here...
4228     setDisabled : function(state)
4229     {
4230         this.disabled = state;
4231         if (!state ) {
4232             this.el.removeClass('disabled');
4233         } else if (!this.el.hasClass('disabled')) {
4234             this.el.addClass('disabled');
4235         }
4236         
4237     },
4238     
4239     /**
4240      * Fetch the element to display the tooltip on.
4241      * @return {Roo.Element} defaults to this.el
4242      */
4243     tooltipEl : function()
4244     {
4245         return this.el.select('' + this.tagtype + '', true).first();
4246     },
4247     
4248     scrollToElement : function(e)
4249     {
4250         var c = document.body;
4251         
4252         /*
4253          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4254          */
4255         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4256             c = document.documentElement;
4257         }
4258         
4259         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4260         
4261         if(!target){
4262             return;
4263         }
4264
4265         var o = target.calcOffsetsTo(c);
4266         
4267         var options = {
4268             target : target,
4269             value : o[1]
4270         }
4271         
4272         this.fireEvent('scrollto', this, options, e);
4273         
4274         Roo.get(c).scrollTo('top', options.value, true);
4275         
4276         return;
4277     }
4278 });
4279  
4280
4281  /*
4282  * - LGPL
4283  *
4284  * sidebar item
4285  *
4286  *  li
4287  *    <span> icon </span>
4288  *    <span> text </span>
4289  *    <span>badge </span>
4290  */
4291
4292 /**
4293  * @class Roo.bootstrap.NavSidebarItem
4294  * @extends Roo.bootstrap.NavItem
4295  * Bootstrap Navbar.NavSidebarItem class
4296  * @constructor
4297  * Create a new Navbar Button
4298  * @param {Object} config The config object
4299  */
4300 Roo.bootstrap.NavSidebarItem = function(config){
4301     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4302     this.addEvents({
4303         // raw events
4304         /**
4305          * @event click
4306          * The raw click event for the entire grid.
4307          * @param {Roo.EventObject} e
4308          */
4309         "click" : true,
4310          /**
4311             * @event changed
4312             * Fires when the active item active state changes
4313             * @param {Roo.bootstrap.NavSidebarItem} this
4314             * @param {boolean} state the new state
4315              
4316          */
4317         'changed': true
4318     });
4319    
4320 };
4321
4322 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4323     
4324     
4325     getAutoCreate : function(){
4326         
4327         
4328         var a = {
4329                 tag: 'a',
4330                 href : this.href || '#',
4331                 cls: '',
4332                 html : '',
4333                 cn : []
4334         };
4335         var cfg = {
4336             tag: 'li',
4337             cls: '',
4338             cn: [ a ]
4339         }
4340         var span = {
4341             tag: 'span',
4342             html : this.html || ''
4343         }
4344         
4345         
4346         if (this.active) {
4347             cfg.cls += ' active';
4348         }
4349         
4350         // left icon..
4351         if (this.glyphicon || this.icon) {
4352             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4353             a.cn.push({ tag : 'i', cls : c }) ;
4354         }
4355         // html..
4356         a.cn.push(span);
4357         // then badge..
4358         if (this.badge !== '') {
4359             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4360         }
4361         // fi
4362         if (this.menu) {
4363             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4364             a.cls += 'dropdown-toggle treeview' ;
4365             
4366         }
4367         
4368         
4369         
4370         return cfg;
4371          
4372            
4373     }
4374    
4375      
4376  
4377 });
4378  
4379
4380  /*
4381  * - LGPL
4382  *
4383  * row
4384  * 
4385  */
4386
4387 /**
4388  * @class Roo.bootstrap.Row
4389  * @extends Roo.bootstrap.Component
4390  * Bootstrap Row class (contains columns...)
4391  * 
4392  * @constructor
4393  * Create a new Row
4394  * @param {Object} config The config object
4395  */
4396
4397 Roo.bootstrap.Row = function(config){
4398     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4399 };
4400
4401 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4402     
4403     getAutoCreate : function(){
4404        return {
4405             cls: 'row clearfix'
4406        };
4407     }
4408     
4409     
4410 });
4411
4412  
4413
4414  /*
4415  * - LGPL
4416  *
4417  * element
4418  * 
4419  */
4420
4421 /**
4422  * @class Roo.bootstrap.Element
4423  * @extends Roo.bootstrap.Component
4424  * Bootstrap Element class
4425  * @cfg {String} html contents of the element
4426  * @cfg {String} tag tag of the element
4427  * @cfg {String} cls class of the element
4428  * @cfg {Boolean} preventDefault (true|false) default false
4429  * @cfg {Boolean} clickable (true|false) default false
4430  * 
4431  * @constructor
4432  * Create a new Element
4433  * @param {Object} config The config object
4434  */
4435
4436 Roo.bootstrap.Element = function(config){
4437     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4438     
4439     this.addEvents({
4440         // raw events
4441         /**
4442          * @event click
4443          * When a element is chick
4444          * @param {Roo.bootstrap.Element} this
4445          * @param {Roo.EventObject} e
4446          */
4447         "click" : true
4448     });
4449 };
4450
4451 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4452     
4453     tag: 'div',
4454     cls: '',
4455     html: '',
4456     preventDefault: false, 
4457     clickable: false,
4458     
4459     getAutoCreate : function(){
4460         
4461         var cfg = {
4462             tag: this.tag,
4463             cls: this.cls,
4464             html: this.html
4465         }
4466         
4467         return cfg;
4468     },
4469     
4470     initEvents: function() 
4471     {
4472         Roo.bootstrap.Element.superclass.initEvents.call(this);
4473         
4474         if(this.clickable){
4475             this.el.on('click', this.onClick, this);
4476         }
4477         
4478     },
4479     
4480     onClick : function(e)
4481     {
4482         if(this.preventDefault){
4483             e.preventDefault();
4484         }
4485         
4486         this.fireEvent('click', this, e);
4487     },
4488     
4489     getValue : function()
4490     {
4491         return this.el.dom.innerHTML;
4492     },
4493     
4494     setValue : function(value)
4495     {
4496         this.el.dom.innerHTML = value;
4497     }
4498    
4499 });
4500
4501  
4502
4503  /*
4504  * - LGPL
4505  *
4506  * pagination
4507  * 
4508  */
4509
4510 /**
4511  * @class Roo.bootstrap.Pagination
4512  * @extends Roo.bootstrap.Component
4513  * Bootstrap Pagination class
4514  * @cfg {String} size xs | sm | md | lg
4515  * @cfg {Boolean} inverse false | true
4516  * 
4517  * @constructor
4518  * Create a new Pagination
4519  * @param {Object} config The config object
4520  */
4521
4522 Roo.bootstrap.Pagination = function(config){
4523     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4524 };
4525
4526 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4527     
4528     cls: false,
4529     size: false,
4530     inverse: false,
4531     
4532     getAutoCreate : function(){
4533         var cfg = {
4534             tag: 'ul',
4535                 cls: 'pagination'
4536         };
4537         if (this.inverse) {
4538             cfg.cls += ' inverse';
4539         }
4540         if (this.html) {
4541             cfg.html=this.html;
4542         }
4543         if (this.cls) {
4544             cfg.cls += " " + this.cls;
4545         }
4546         return cfg;
4547     }
4548    
4549 });
4550
4551  
4552
4553  /*
4554  * - LGPL
4555  *
4556  * Pagination item
4557  * 
4558  */
4559
4560
4561 /**
4562  * @class Roo.bootstrap.PaginationItem
4563  * @extends Roo.bootstrap.Component
4564  * Bootstrap PaginationItem class
4565  * @cfg {String} html text
4566  * @cfg {String} href the link
4567  * @cfg {Boolean} preventDefault (true | false) default true
4568  * @cfg {Boolean} active (true | false) default false
4569  * @cfg {Boolean} disabled default false
4570  * 
4571  * 
4572  * @constructor
4573  * Create a new PaginationItem
4574  * @param {Object} config The config object
4575  */
4576
4577
4578 Roo.bootstrap.PaginationItem = function(config){
4579     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4580     this.addEvents({
4581         // raw events
4582         /**
4583          * @event click
4584          * The raw click event for the entire grid.
4585          * @param {Roo.EventObject} e
4586          */
4587         "click" : true
4588     });
4589 };
4590
4591 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4592     
4593     href : false,
4594     html : false,
4595     preventDefault: true,
4596     active : false,
4597     cls : false,
4598     disabled: false,
4599     
4600     getAutoCreate : function(){
4601         var cfg= {
4602             tag: 'li',
4603             cn: [
4604                 {
4605                     tag : 'a',
4606                     href : this.href ? this.href : '#',
4607                     html : this.html ? this.html : ''
4608                 }
4609             ]
4610         };
4611         
4612         if(this.cls){
4613             cfg.cls = this.cls;
4614         }
4615         
4616         if(this.disabled){
4617             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4618         }
4619         
4620         if(this.active){
4621             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4622         }
4623         
4624         return cfg;
4625     },
4626     
4627     initEvents: function() {
4628         
4629         this.el.on('click', this.onClick, this);
4630         
4631     },
4632     onClick : function(e)
4633     {
4634         Roo.log('PaginationItem on click ');
4635         if(this.preventDefault){
4636             e.preventDefault();
4637         }
4638         
4639         if(this.disabled){
4640             return;
4641         }
4642         
4643         this.fireEvent('click', this, e);
4644     }
4645    
4646 });
4647
4648  
4649
4650  /*
4651  * - LGPL
4652  *
4653  * slider
4654  * 
4655  */
4656
4657
4658 /**
4659  * @class Roo.bootstrap.Slider
4660  * @extends Roo.bootstrap.Component
4661  * Bootstrap Slider class
4662  *    
4663  * @constructor
4664  * Create a new Slider
4665  * @param {Object} config The config object
4666  */
4667
4668 Roo.bootstrap.Slider = function(config){
4669     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4670 };
4671
4672 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4673     
4674     getAutoCreate : function(){
4675         
4676         var cfg = {
4677             tag: 'div',
4678             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4679             cn: [
4680                 {
4681                     tag: 'a',
4682                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4683                 }
4684             ]
4685         }
4686         
4687         return cfg;
4688     }
4689    
4690 });
4691
4692  /*
4693  * Based on:
4694  * Ext JS Library 1.1.1
4695  * Copyright(c) 2006-2007, Ext JS, LLC.
4696  *
4697  * Originally Released Under LGPL - original licence link has changed is not relivant.
4698  *
4699  * Fork - LGPL
4700  * <script type="text/javascript">
4701  */
4702  
4703
4704 /**
4705  * @class Roo.grid.ColumnModel
4706  * @extends Roo.util.Observable
4707  * This is the default implementation of a ColumnModel used by the Grid. It defines
4708  * the columns in the grid.
4709  * <br>Usage:<br>
4710  <pre><code>
4711  var colModel = new Roo.grid.ColumnModel([
4712         {header: "Ticker", width: 60, sortable: true, locked: true},
4713         {header: "Company Name", width: 150, sortable: true},
4714         {header: "Market Cap.", width: 100, sortable: true},
4715         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4716         {header: "Employees", width: 100, sortable: true, resizable: false}
4717  ]);
4718  </code></pre>
4719  * <p>
4720  
4721  * The config options listed for this class are options which may appear in each
4722  * individual column definition.
4723  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4724  * @constructor
4725  * @param {Object} config An Array of column config objects. See this class's
4726  * config objects for details.
4727 */
4728 Roo.grid.ColumnModel = function(config){
4729         /**
4730      * The config passed into the constructor
4731      */
4732     this.config = config;
4733     this.lookup = {};
4734
4735     // if no id, create one
4736     // if the column does not have a dataIndex mapping,
4737     // map it to the order it is in the config
4738     for(var i = 0, len = config.length; i < len; i++){
4739         var c = config[i];
4740         if(typeof c.dataIndex == "undefined"){
4741             c.dataIndex = i;
4742         }
4743         if(typeof c.renderer == "string"){
4744             c.renderer = Roo.util.Format[c.renderer];
4745         }
4746         if(typeof c.id == "undefined"){
4747             c.id = Roo.id();
4748         }
4749         if(c.editor && c.editor.xtype){
4750             c.editor  = Roo.factory(c.editor, Roo.grid);
4751         }
4752         if(c.editor && c.editor.isFormField){
4753             c.editor = new Roo.grid.GridEditor(c.editor);
4754         }
4755         this.lookup[c.id] = c;
4756     }
4757
4758     /**
4759      * The width of columns which have no width specified (defaults to 100)
4760      * @type Number
4761      */
4762     this.defaultWidth = 100;
4763
4764     /**
4765      * Default sortable of columns which have no sortable specified (defaults to false)
4766      * @type Boolean
4767      */
4768     this.defaultSortable = false;
4769
4770     this.addEvents({
4771         /**
4772              * @event widthchange
4773              * Fires when the width of a column changes.
4774              * @param {ColumnModel} this
4775              * @param {Number} columnIndex The column index
4776              * @param {Number} newWidth The new width
4777              */
4778             "widthchange": true,
4779         /**
4780              * @event headerchange
4781              * Fires when the text of a header changes.
4782              * @param {ColumnModel} this
4783              * @param {Number} columnIndex The column index
4784              * @param {Number} newText The new header text
4785              */
4786             "headerchange": true,
4787         /**
4788              * @event hiddenchange
4789              * Fires when a column is hidden or "unhidden".
4790              * @param {ColumnModel} this
4791              * @param {Number} columnIndex The column index
4792              * @param {Boolean} hidden true if hidden, false otherwise
4793              */
4794             "hiddenchange": true,
4795             /**
4796          * @event columnmoved
4797          * Fires when a column is moved.
4798          * @param {ColumnModel} this
4799          * @param {Number} oldIndex
4800          * @param {Number} newIndex
4801          */
4802         "columnmoved" : true,
4803         /**
4804          * @event columlockchange
4805          * Fires when a column's locked state is changed
4806          * @param {ColumnModel} this
4807          * @param {Number} colIndex
4808          * @param {Boolean} locked true if locked
4809          */
4810         "columnlockchange" : true
4811     });
4812     Roo.grid.ColumnModel.superclass.constructor.call(this);
4813 };
4814 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4815     /**
4816      * @cfg {String} header The header text to display in the Grid view.
4817      */
4818     /**
4819      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4820      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4821      * specified, the column's index is used as an index into the Record's data Array.
4822      */
4823     /**
4824      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4825      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4826      */
4827     /**
4828      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4829      * Defaults to the value of the {@link #defaultSortable} property.
4830      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4831      */
4832     /**
4833      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4834      */
4835     /**
4836      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4837      */
4838     /**
4839      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4840      */
4841     /**
4842      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4843      */
4844     /**
4845      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4846      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4847      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4848      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4849      */
4850        /**
4851      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4852      */
4853     /**
4854      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4855      */
4856     /**
4857      * @cfg {String} cursor (Optional)
4858      */
4859     /**
4860      * @cfg {String} tooltip (Optional)
4861      */
4862     /**
4863      * Returns the id of the column at the specified index.
4864      * @param {Number} index The column index
4865      * @return {String} the id
4866      */
4867     getColumnId : function(index){
4868         return this.config[index].id;
4869     },
4870
4871     /**
4872      * Returns the column for a specified id.
4873      * @param {String} id The column id
4874      * @return {Object} the column
4875      */
4876     getColumnById : function(id){
4877         return this.lookup[id];
4878     },
4879
4880     
4881     /**
4882      * Returns the column for a specified dataIndex.
4883      * @param {String} dataIndex The column dataIndex
4884      * @return {Object|Boolean} the column or false if not found
4885      */
4886     getColumnByDataIndex: function(dataIndex){
4887         var index = this.findColumnIndex(dataIndex);
4888         return index > -1 ? this.config[index] : false;
4889     },
4890     
4891     /**
4892      * Returns the index for a specified column id.
4893      * @param {String} id The column id
4894      * @return {Number} the index, or -1 if not found
4895      */
4896     getIndexById : function(id){
4897         for(var i = 0, len = this.config.length; i < len; i++){
4898             if(this.config[i].id == id){
4899                 return i;
4900             }
4901         }
4902         return -1;
4903     },
4904     
4905     /**
4906      * Returns the index for a specified column dataIndex.
4907      * @param {String} dataIndex The column dataIndex
4908      * @return {Number} the index, or -1 if not found
4909      */
4910     
4911     findColumnIndex : function(dataIndex){
4912         for(var i = 0, len = this.config.length; i < len; i++){
4913             if(this.config[i].dataIndex == dataIndex){
4914                 return i;
4915             }
4916         }
4917         return -1;
4918     },
4919     
4920     
4921     moveColumn : function(oldIndex, newIndex){
4922         var c = this.config[oldIndex];
4923         this.config.splice(oldIndex, 1);
4924         this.config.splice(newIndex, 0, c);
4925         this.dataMap = null;
4926         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4927     },
4928
4929     isLocked : function(colIndex){
4930         return this.config[colIndex].locked === true;
4931     },
4932
4933     setLocked : function(colIndex, value, suppressEvent){
4934         if(this.isLocked(colIndex) == value){
4935             return;
4936         }
4937         this.config[colIndex].locked = value;
4938         if(!suppressEvent){
4939             this.fireEvent("columnlockchange", this, colIndex, value);
4940         }
4941     },
4942
4943     getTotalLockedWidth : function(){
4944         var totalWidth = 0;
4945         for(var i = 0; i < this.config.length; i++){
4946             if(this.isLocked(i) && !this.isHidden(i)){
4947                 this.totalWidth += this.getColumnWidth(i);
4948             }
4949         }
4950         return totalWidth;
4951     },
4952
4953     getLockedCount : function(){
4954         for(var i = 0, len = this.config.length; i < len; i++){
4955             if(!this.isLocked(i)){
4956                 return i;
4957             }
4958         }
4959     },
4960
4961     /**
4962      * Returns the number of columns.
4963      * @return {Number}
4964      */
4965     getColumnCount : function(visibleOnly){
4966         if(visibleOnly === true){
4967             var c = 0;
4968             for(var i = 0, len = this.config.length; i < len; i++){
4969                 if(!this.isHidden(i)){
4970                     c++;
4971                 }
4972             }
4973             return c;
4974         }
4975         return this.config.length;
4976     },
4977
4978     /**
4979      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4980      * @param {Function} fn
4981      * @param {Object} scope (optional)
4982      * @return {Array} result
4983      */
4984     getColumnsBy : function(fn, scope){
4985         var r = [];
4986         for(var i = 0, len = this.config.length; i < len; i++){
4987             var c = this.config[i];
4988             if(fn.call(scope||this, c, i) === true){
4989                 r[r.length] = c;
4990             }
4991         }
4992         return r;
4993     },
4994
4995     /**
4996      * Returns true if the specified column is sortable.
4997      * @param {Number} col The column index
4998      * @return {Boolean}
4999      */
5000     isSortable : function(col){
5001         if(typeof this.config[col].sortable == "undefined"){
5002             return this.defaultSortable;
5003         }
5004         return this.config[col].sortable;
5005     },
5006
5007     /**
5008      * Returns the rendering (formatting) function defined for the column.
5009      * @param {Number} col The column index.
5010      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5011      */
5012     getRenderer : function(col){
5013         if(!this.config[col].renderer){
5014             return Roo.grid.ColumnModel.defaultRenderer;
5015         }
5016         return this.config[col].renderer;
5017     },
5018
5019     /**
5020      * Sets the rendering (formatting) function for a column.
5021      * @param {Number} col The column index
5022      * @param {Function} fn The function to use to process the cell's raw data
5023      * to return HTML markup for the grid view. The render function is called with
5024      * the following parameters:<ul>
5025      * <li>Data value.</li>
5026      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5027      * <li>css A CSS style string to apply to the table cell.</li>
5028      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5029      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5030      * <li>Row index</li>
5031      * <li>Column index</li>
5032      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5033      */
5034     setRenderer : function(col, fn){
5035         this.config[col].renderer = fn;
5036     },
5037
5038     /**
5039      * Returns the width for the specified column.
5040      * @param {Number} col The column index
5041      * @return {Number}
5042      */
5043     getColumnWidth : function(col){
5044         return this.config[col].width * 1 || this.defaultWidth;
5045     },
5046
5047     /**
5048      * Sets the width for a column.
5049      * @param {Number} col The column index
5050      * @param {Number} width The new width
5051      */
5052     setColumnWidth : function(col, width, suppressEvent){
5053         this.config[col].width = width;
5054         this.totalWidth = null;
5055         if(!suppressEvent){
5056              this.fireEvent("widthchange", this, col, width);
5057         }
5058     },
5059
5060     /**
5061      * Returns the total width of all columns.
5062      * @param {Boolean} includeHidden True to include hidden column widths
5063      * @return {Number}
5064      */
5065     getTotalWidth : function(includeHidden){
5066         if(!this.totalWidth){
5067             this.totalWidth = 0;
5068             for(var i = 0, len = this.config.length; i < len; i++){
5069                 if(includeHidden || !this.isHidden(i)){
5070                     this.totalWidth += this.getColumnWidth(i);
5071                 }
5072             }
5073         }
5074         return this.totalWidth;
5075     },
5076
5077     /**
5078      * Returns the header for the specified column.
5079      * @param {Number} col The column index
5080      * @return {String}
5081      */
5082     getColumnHeader : function(col){
5083         return this.config[col].header;
5084     },
5085
5086     /**
5087      * Sets the header for a column.
5088      * @param {Number} col The column index
5089      * @param {String} header The new header
5090      */
5091     setColumnHeader : function(col, header){
5092         this.config[col].header = header;
5093         this.fireEvent("headerchange", this, col, header);
5094     },
5095
5096     /**
5097      * Returns the tooltip for the specified column.
5098      * @param {Number} col The column index
5099      * @return {String}
5100      */
5101     getColumnTooltip : function(col){
5102             return this.config[col].tooltip;
5103     },
5104     /**
5105      * Sets the tooltip for a column.
5106      * @param {Number} col The column index
5107      * @param {String} tooltip The new tooltip
5108      */
5109     setColumnTooltip : function(col, tooltip){
5110             this.config[col].tooltip = tooltip;
5111     },
5112
5113     /**
5114      * Returns the dataIndex for the specified column.
5115      * @param {Number} col The column index
5116      * @return {Number}
5117      */
5118     getDataIndex : function(col){
5119         return this.config[col].dataIndex;
5120     },
5121
5122     /**
5123      * Sets the dataIndex for a column.
5124      * @param {Number} col The column index
5125      * @param {Number} dataIndex The new dataIndex
5126      */
5127     setDataIndex : function(col, dataIndex){
5128         this.config[col].dataIndex = dataIndex;
5129     },
5130
5131     
5132     
5133     /**
5134      * Returns true if the cell is editable.
5135      * @param {Number} colIndex The column index
5136      * @param {Number} rowIndex The row index
5137      * @return {Boolean}
5138      */
5139     isCellEditable : function(colIndex, rowIndex){
5140         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5141     },
5142
5143     /**
5144      * Returns the editor defined for the cell/column.
5145      * return false or null to disable editing.
5146      * @param {Number} colIndex The column index
5147      * @param {Number} rowIndex The row index
5148      * @return {Object}
5149      */
5150     getCellEditor : function(colIndex, rowIndex){
5151         return this.config[colIndex].editor;
5152     },
5153
5154     /**
5155      * Sets if a column is editable.
5156      * @param {Number} col The column index
5157      * @param {Boolean} editable True if the column is editable
5158      */
5159     setEditable : function(col, editable){
5160         this.config[col].editable = editable;
5161     },
5162
5163
5164     /**
5165      * Returns true if the column is hidden.
5166      * @param {Number} colIndex The column index
5167      * @return {Boolean}
5168      */
5169     isHidden : function(colIndex){
5170         return this.config[colIndex].hidden;
5171     },
5172
5173
5174     /**
5175      * Returns true if the column width cannot be changed
5176      */
5177     isFixed : function(colIndex){
5178         return this.config[colIndex].fixed;
5179     },
5180
5181     /**
5182      * Returns true if the column can be resized
5183      * @return {Boolean}
5184      */
5185     isResizable : function(colIndex){
5186         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5187     },
5188     /**
5189      * Sets if a column is hidden.
5190      * @param {Number} colIndex The column index
5191      * @param {Boolean} hidden True if the column is hidden
5192      */
5193     setHidden : function(colIndex, hidden){
5194         this.config[colIndex].hidden = hidden;
5195         this.totalWidth = null;
5196         this.fireEvent("hiddenchange", this, colIndex, hidden);
5197     },
5198
5199     /**
5200      * Sets the editor for a column.
5201      * @param {Number} col The column index
5202      * @param {Object} editor The editor object
5203      */
5204     setEditor : function(col, editor){
5205         this.config[col].editor = editor;
5206     }
5207 });
5208
5209 Roo.grid.ColumnModel.defaultRenderer = function(value){
5210         if(typeof value == "string" && value.length < 1){
5211             return "&#160;";
5212         }
5213         return value;
5214 };
5215
5216 // Alias for backwards compatibility
5217 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5218 /*
5219  * Based on:
5220  * Ext JS Library 1.1.1
5221  * Copyright(c) 2006-2007, Ext JS, LLC.
5222  *
5223  * Originally Released Under LGPL - original licence link has changed is not relivant.
5224  *
5225  * Fork - LGPL
5226  * <script type="text/javascript">
5227  */
5228  
5229 /**
5230  * @class Roo.LoadMask
5231  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5232  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5233  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5234  * element's UpdateManager load indicator and will be destroyed after the initial load.
5235  * @constructor
5236  * Create a new LoadMask
5237  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5238  * @param {Object} config The config object
5239  */
5240 Roo.LoadMask = function(el, config){
5241     this.el = Roo.get(el);
5242     Roo.apply(this, config);
5243     if(this.store){
5244         this.store.on('beforeload', this.onBeforeLoad, this);
5245         this.store.on('load', this.onLoad, this);
5246         this.store.on('loadexception', this.onLoadException, this);
5247         this.removeMask = false;
5248     }else{
5249         var um = this.el.getUpdateManager();
5250         um.showLoadIndicator = false; // disable the default indicator
5251         um.on('beforeupdate', this.onBeforeLoad, this);
5252         um.on('update', this.onLoad, this);
5253         um.on('failure', this.onLoad, this);
5254         this.removeMask = true;
5255     }
5256 };
5257
5258 Roo.LoadMask.prototype = {
5259     /**
5260      * @cfg {Boolean} removeMask
5261      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5262      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5263      */
5264     /**
5265      * @cfg {String} msg
5266      * The text to display in a centered loading message box (defaults to 'Loading...')
5267      */
5268     msg : 'Loading...',
5269     /**
5270      * @cfg {String} msgCls
5271      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5272      */
5273     msgCls : 'x-mask-loading',
5274
5275     /**
5276      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5277      * @type Boolean
5278      */
5279     disabled: false,
5280
5281     /**
5282      * Disables the mask to prevent it from being displayed
5283      */
5284     disable : function(){
5285        this.disabled = true;
5286     },
5287
5288     /**
5289      * Enables the mask so that it can be displayed
5290      */
5291     enable : function(){
5292         this.disabled = false;
5293     },
5294     
5295     onLoadException : function()
5296     {
5297         Roo.log(arguments);
5298         
5299         if (typeof(arguments[3]) != 'undefined') {
5300             Roo.MessageBox.alert("Error loading",arguments[3]);
5301         } 
5302         /*
5303         try {
5304             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5305                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5306             }   
5307         } catch(e) {
5308             
5309         }
5310         */
5311     
5312         
5313         
5314         this.el.unmask(this.removeMask);
5315     },
5316     // private
5317     onLoad : function()
5318     {
5319         this.el.unmask(this.removeMask);
5320     },
5321
5322     // private
5323     onBeforeLoad : function(){
5324         if(!this.disabled){
5325             this.el.mask(this.msg, this.msgCls);
5326         }
5327     },
5328
5329     // private
5330     destroy : function(){
5331         if(this.store){
5332             this.store.un('beforeload', this.onBeforeLoad, this);
5333             this.store.un('load', this.onLoad, this);
5334             this.store.un('loadexception', this.onLoadException, this);
5335         }else{
5336             var um = this.el.getUpdateManager();
5337             um.un('beforeupdate', this.onBeforeLoad, this);
5338             um.un('update', this.onLoad, this);
5339             um.un('failure', this.onLoad, this);
5340         }
5341     }
5342 };/*
5343  * - LGPL
5344  *
5345  * table
5346  * 
5347  */
5348
5349 /**
5350  * @class Roo.bootstrap.Table
5351  * @extends Roo.bootstrap.Component
5352  * Bootstrap Table class
5353  * @cfg {String} cls table class
5354  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5355  * @cfg {String} bgcolor Specifies the background color for a table
5356  * @cfg {Number} border Specifies whether the table cells should have borders or not
5357  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5358  * @cfg {Number} cellspacing Specifies the space between cells
5359  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5360  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5361  * @cfg {String} sortable Specifies that the table should be sortable
5362  * @cfg {String} summary Specifies a summary of the content of a table
5363  * @cfg {Number} width Specifies the width of a table
5364  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5365  * 
5366  * @cfg {boolean} striped Should the rows be alternative striped
5367  * @cfg {boolean} bordered Add borders to the table
5368  * @cfg {boolean} hover Add hover highlighting
5369  * @cfg {boolean} condensed Format condensed
5370  * @cfg {boolean} responsive Format condensed
5371  * @cfg {Boolean} loadMask (true|false) default false
5372  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5373  * @cfg {Boolean} thead (true|false) generate thead, default true
5374  * @cfg {Boolean} RowSelection (true|false) default false
5375  * @cfg {Boolean} CellSelection (true|false) default false
5376  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5377  
5378  * 
5379  * @constructor
5380  * Create a new Table
5381  * @param {Object} config The config object
5382  */
5383
5384 Roo.bootstrap.Table = function(config){
5385     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5386     
5387     if (this.sm) {
5388         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5389         this.sm = this.selModel;
5390         this.sm.xmodule = this.xmodule || false;
5391     }
5392     if (this.cm && typeof(this.cm.config) == 'undefined') {
5393         this.colModel = new Roo.grid.ColumnModel(this.cm);
5394         this.cm = this.colModel;
5395         this.cm.xmodule = this.xmodule || false;
5396     }
5397     if (this.store) {
5398         this.store= Roo.factory(this.store, Roo.data);
5399         this.ds = this.store;
5400         this.ds.xmodule = this.xmodule || false;
5401          
5402     }
5403     if (this.footer && this.store) {
5404         this.footer.dataSource = this.ds;
5405         this.footer = Roo.factory(this.footer);
5406     }
5407     
5408     /** @private */
5409     this.addEvents({
5410         /**
5411          * @event cellclick
5412          * Fires when a cell is clicked
5413          * @param {Roo.bootstrap.Table} this
5414          * @param {Roo.Element} el
5415          * @param {Number} rowIndex
5416          * @param {Number} columnIndex
5417          * @param {Roo.EventObject} e
5418          */
5419         "cellclick" : true,
5420         /**
5421          * @event celldblclick
5422          * Fires when a cell is double clicked
5423          * @param {Roo.bootstrap.Table} this
5424          * @param {Roo.Element} el
5425          * @param {Number} rowIndex
5426          * @param {Number} columnIndex
5427          * @param {Roo.EventObject} e
5428          */
5429         "celldblclick" : true,
5430         /**
5431          * @event rowclick
5432          * Fires when a row is clicked
5433          * @param {Roo.bootstrap.Table} this
5434          * @param {Roo.Element} el
5435          * @param {Number} rowIndex
5436          * @param {Roo.EventObject} e
5437          */
5438         "rowclick" : true,
5439         /**
5440          * @event rowdblclick
5441          * Fires when a row is double clicked
5442          * @param {Roo.bootstrap.Table} this
5443          * @param {Roo.Element} el
5444          * @param {Number} rowIndex
5445          * @param {Roo.EventObject} e
5446          */
5447         "rowdblclick" : true,
5448         /**
5449          * @event mouseover
5450          * Fires when a mouseover occur
5451          * @param {Roo.bootstrap.Table} this
5452          * @param {Roo.Element} el
5453          * @param {Number} rowIndex
5454          * @param {Number} columnIndex
5455          * @param {Roo.EventObject} e
5456          */
5457         "mouseover" : true,
5458         /**
5459          * @event mouseout
5460          * Fires when a mouseout occur
5461          * @param {Roo.bootstrap.Table} this
5462          * @param {Roo.Element} el
5463          * @param {Number} rowIndex
5464          * @param {Number} columnIndex
5465          * @param {Roo.EventObject} e
5466          */
5467         "mouseout" : true,
5468         /**
5469          * @event rowclass
5470          * Fires when a row is rendered, so you can change add a style to it.
5471          * @param {Roo.bootstrap.Table} this
5472          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5473          */
5474         'rowclass' : true,
5475           /**
5476          * @event rowsrendered
5477          * Fires when all the  rows have been rendered
5478          * @param {Roo.bootstrap.Table} this
5479          */
5480         'rowsrendered' : true
5481         
5482     });
5483 };
5484
5485 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5486     
5487     cls: false,
5488     align: false,
5489     bgcolor: false,
5490     border: false,
5491     cellpadding: false,
5492     cellspacing: false,
5493     frame: false,
5494     rules: false,
5495     sortable: false,
5496     summary: false,
5497     width: false,
5498     striped : false,
5499     bordered: false,
5500     hover:  false,
5501     condensed : false,
5502     responsive : false,
5503     sm : false,
5504     cm : false,
5505     store : false,
5506     loadMask : false,
5507     tfoot : true,
5508     thead : true,
5509     RowSelection : false,
5510     CellSelection : false,
5511     layout : false,
5512     
5513     // Roo.Element - the tbody
5514     mainBody: false, 
5515     
5516     getAutoCreate : function(){
5517         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5518         
5519         cfg = {
5520             tag: 'table',
5521             cls : 'table',
5522             cn : []
5523         }
5524             
5525         if (this.striped) {
5526             cfg.cls += ' table-striped';
5527         }
5528         
5529         if (this.hover) {
5530             cfg.cls += ' table-hover';
5531         }
5532         if (this.bordered) {
5533             cfg.cls += ' table-bordered';
5534         }
5535         if (this.condensed) {
5536             cfg.cls += ' table-condensed';
5537         }
5538         if (this.responsive) {
5539             cfg.cls += ' table-responsive';
5540         }
5541         
5542         if (this.cls) {
5543             cfg.cls+=  ' ' +this.cls;
5544         }
5545         
5546         // this lot should be simplifed...
5547         
5548         if (this.align) {
5549             cfg.align=this.align;
5550         }
5551         if (this.bgcolor) {
5552             cfg.bgcolor=this.bgcolor;
5553         }
5554         if (this.border) {
5555             cfg.border=this.border;
5556         }
5557         if (this.cellpadding) {
5558             cfg.cellpadding=this.cellpadding;
5559         }
5560         if (this.cellspacing) {
5561             cfg.cellspacing=this.cellspacing;
5562         }
5563         if (this.frame) {
5564             cfg.frame=this.frame;
5565         }
5566         if (this.rules) {
5567             cfg.rules=this.rules;
5568         }
5569         if (this.sortable) {
5570             cfg.sortable=this.sortable;
5571         }
5572         if (this.summary) {
5573             cfg.summary=this.summary;
5574         }
5575         if (this.width) {
5576             cfg.width=this.width;
5577         }
5578         if (this.layout) {
5579             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5580         }
5581         
5582         if(this.store || this.cm){
5583             if(this.thead){
5584                 cfg.cn.push(this.renderHeader());
5585             }
5586             
5587             cfg.cn.push(this.renderBody());
5588             
5589             if(this.tfoot){
5590                 cfg.cn.push(this.renderFooter());
5591             }
5592             
5593             cfg.cls+=  ' TableGrid';
5594         }
5595         
5596         return { cn : [ cfg ] };
5597     },
5598     
5599     initEvents : function()
5600     {   
5601         if(!this.store || !this.cm){
5602             return;
5603         }
5604         
5605         //Roo.log('initEvents with ds!!!!');
5606         
5607         this.mainBody = this.el.select('tbody', true).first();
5608         
5609         
5610         var _this = this;
5611         
5612         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5613             e.on('click', _this.sort, _this);
5614         });
5615         
5616         this.el.on("click", this.onClick, this);
5617         this.el.on("dblclick", this.onDblClick, this);
5618         
5619         // why is this done????? = it breaks dialogs??
5620         //this.parent().el.setStyle('position', 'relative');
5621         
5622         
5623         if (this.footer) {
5624             this.footer.parentId = this.id;
5625             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5626         }
5627         
5628         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5629         
5630         this.store.on('load', this.onLoad, this);
5631         this.store.on('beforeload', this.onBeforeLoad, this);
5632         this.store.on('update', this.onUpdate, this);
5633         this.store.on('add', this.onAdd, this);
5634         
5635     },
5636     
5637     onMouseover : function(e, el)
5638     {
5639         var cell = Roo.get(el);
5640         
5641         if(!cell){
5642             return;
5643         }
5644         
5645         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5646             cell = cell.findParent('td', false, true);
5647         }
5648         
5649         var row = cell.findParent('tr', false, true);
5650         var cellIndex = cell.dom.cellIndex;
5651         var rowIndex = row.dom.rowIndex - 1; // start from 0
5652         
5653         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5654         
5655     },
5656     
5657     onMouseout : function(e, el)
5658     {
5659         var cell = Roo.get(el);
5660         
5661         if(!cell){
5662             return;
5663         }
5664         
5665         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5666             cell = cell.findParent('td', false, true);
5667         }
5668         
5669         var row = cell.findParent('tr', false, true);
5670         var cellIndex = cell.dom.cellIndex;
5671         var rowIndex = row.dom.rowIndex - 1; // start from 0
5672         
5673         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5674         
5675     },
5676     
5677     onClick : function(e, el)
5678     {
5679         var cell = Roo.get(el);
5680         
5681         if(!cell || (!this.CellSelection && !this.RowSelection)){
5682             return;
5683         }
5684         
5685         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5686             cell = cell.findParent('td', false, true);
5687         }
5688         
5689         if(!cell || typeof(cell) == 'undefined'){
5690             return;
5691         }
5692         
5693         var row = cell.findParent('tr', false, true);
5694         
5695         if(!row || typeof(row) == 'undefined'){
5696             return;
5697         }
5698         
5699         var cellIndex = cell.dom.cellIndex;
5700         var rowIndex = this.getRowIndex(row);
5701         
5702         if(this.CellSelection){
5703             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5704         }
5705         
5706         if(this.RowSelection){
5707             this.fireEvent('rowclick', this, row, rowIndex, e);
5708         }
5709         
5710         
5711     },
5712     
5713     onDblClick : function(e,el)
5714     {
5715         var cell = Roo.get(el);
5716         
5717         if(!cell || (!this.CellSelection && !this.RowSelection)){
5718             return;
5719         }
5720         
5721         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5722             cell = cell.findParent('td', false, true);
5723         }
5724         
5725         if(!cell || typeof(cell) == 'undefined'){
5726             return;
5727         }
5728         
5729         var row = cell.findParent('tr', false, true);
5730         
5731         if(!row || typeof(row) == 'undefined'){
5732             return;
5733         }
5734         
5735         var cellIndex = cell.dom.cellIndex;
5736         var rowIndex = this.getRowIndex(row);
5737         
5738         if(this.CellSelection){
5739             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5740         }
5741         
5742         if(this.RowSelection){
5743             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5744         }
5745     },
5746     
5747     sort : function(e,el)
5748     {
5749         var col = Roo.get(el);
5750         
5751         if(!col.hasClass('sortable')){
5752             return;
5753         }
5754         
5755         var sort = col.attr('sort');
5756         var dir = 'ASC';
5757         
5758         if(col.hasClass('glyphicon-arrow-up')){
5759             dir = 'DESC';
5760         }
5761         
5762         this.store.sortInfo = {field : sort, direction : dir};
5763         
5764         if (this.footer) {
5765             Roo.log("calling footer first");
5766             this.footer.onClick('first');
5767         } else {
5768         
5769             this.store.load({ params : { start : 0 } });
5770         }
5771     },
5772     
5773     renderHeader : function()
5774     {
5775         var header = {
5776             tag: 'thead',
5777             cn : []
5778         };
5779         
5780         var cm = this.cm;
5781         
5782         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5783             
5784             var config = cm.config[i];
5785                     
5786             var c = {
5787                 tag: 'th',
5788                 style : '',
5789                 html: cm.getColumnHeader(i)
5790             };
5791             
5792             if(typeof(config.tooltip) != 'undefined'){
5793                 c.tooltip = config.tooltip;
5794             }
5795             
5796             if(typeof(config.colspan) != 'undefined'){
5797                 c.colspan = config.colspan;
5798             }
5799             
5800             if(typeof(config.hidden) != 'undefined' && config.hidden){
5801                 c.style += ' display:none;';
5802             }
5803             
5804             if(typeof(config.dataIndex) != 'undefined'){
5805                 c.sort = config.dataIndex;
5806             }
5807             
5808             if(typeof(config.sortable) != 'undefined' && config.sortable){
5809                 c.cls = 'sortable';
5810             }
5811             
5812             if(typeof(config.align) != 'undefined' && config.align.length){
5813                 c.style += ' text-align:' + config.align + ';';
5814             }
5815             
5816             if(typeof(config.width) != 'undefined'){
5817                 c.style += ' width:' + config.width + 'px;';
5818             }
5819             
5820             if(typeof(config.cls) != 'undefined'){
5821                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5822             }
5823             
5824             header.cn.push(c)
5825         }
5826         
5827         return header;
5828     },
5829     
5830     renderBody : function()
5831     {
5832         var body = {
5833             tag: 'tbody',
5834             cn : [
5835                 {
5836                     tag: 'tr',
5837                     cn : [
5838                         {
5839                             tag : 'td',
5840                             colspan :  this.cm.getColumnCount()
5841                         }
5842                     ]
5843                 }
5844             ]
5845         };
5846         
5847         return body;
5848     },
5849     
5850     renderFooter : function()
5851     {
5852         var footer = {
5853             tag: 'tfoot',
5854             cn : [
5855                 {
5856                     tag: 'tr',
5857                     cn : [
5858                         {
5859                             tag : 'td',
5860                             colspan :  this.cm.getColumnCount()
5861                         }
5862                     ]
5863                 }
5864             ]
5865         };
5866         
5867         return footer;
5868     },
5869     
5870     
5871     
5872     onLoad : function()
5873     {
5874         Roo.log('ds onload');
5875         this.clear();
5876         
5877         var _this = this;
5878         var cm = this.cm;
5879         var ds = this.store;
5880         
5881         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5882             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5883             
5884             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5885                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5886             }
5887             
5888             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5889                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5890             }
5891         });
5892         
5893         var tbody =  this.mainBody;
5894               
5895         if(ds.getCount() > 0){
5896             ds.data.each(function(d,rowIndex){
5897                 var row =  this.renderRow(cm, ds, rowIndex);
5898                 
5899                 tbody.createChild(row);
5900                 
5901                 var _this = this;
5902                 
5903                 if(row.cellObjects.length){
5904                     Roo.each(row.cellObjects, function(r){
5905                         _this.renderCellObject(r);
5906                     })
5907                 }
5908                 
5909             }, this);
5910         }
5911         
5912         Roo.each(this.el.select('tbody td', true).elements, function(e){
5913             e.on('mouseover', _this.onMouseover, _this);
5914         });
5915         
5916         Roo.each(this.el.select('tbody td', true).elements, function(e){
5917             e.on('mouseout', _this.onMouseout, _this);
5918         });
5919         this.fireEvent('rowsrendered', this);
5920         //if(this.loadMask){
5921         //    this.maskEl.hide();
5922         //}
5923     },
5924     
5925     
5926     onUpdate : function(ds,record)
5927     {
5928         this.refreshRow(record);
5929     },
5930     
5931     onRemove : function(ds, record, index, isUpdate){
5932         if(isUpdate !== true){
5933             this.fireEvent("beforerowremoved", this, index, record);
5934         }
5935         var bt = this.mainBody.dom;
5936         
5937         var rows = this.el.select('tbody > tr', true).elements;
5938         
5939         if(typeof(rows[index]) != 'undefined'){
5940             bt.removeChild(rows[index].dom);
5941         }
5942         
5943 //        if(bt.rows[index]){
5944 //            bt.removeChild(bt.rows[index]);
5945 //        }
5946         
5947         if(isUpdate !== true){
5948             //this.stripeRows(index);
5949             //this.syncRowHeights(index, index);
5950             //this.layout();
5951             this.fireEvent("rowremoved", this, index, record);
5952         }
5953     },
5954     
5955     onAdd : function(ds, records, rowIndex)
5956     {
5957         //Roo.log('on Add called');
5958         // - note this does not handle multiple adding very well..
5959         var bt = this.mainBody.dom;
5960         for (var i =0 ; i < records.length;i++) {
5961             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5962             //Roo.log(records[i]);
5963             //Roo.log(this.store.getAt(rowIndex+i));
5964             this.insertRow(this.store, rowIndex + i, false);
5965             return;
5966         }
5967         
5968     },
5969     
5970     
5971     refreshRow : function(record){
5972         var ds = this.store, index;
5973         if(typeof record == 'number'){
5974             index = record;
5975             record = ds.getAt(index);
5976         }else{
5977             index = ds.indexOf(record);
5978         }
5979         this.insertRow(ds, index, true);
5980         this.onRemove(ds, record, index+1, true);
5981         //this.syncRowHeights(index, index);
5982         //this.layout();
5983         this.fireEvent("rowupdated", this, index, record);
5984     },
5985     
5986     insertRow : function(dm, rowIndex, isUpdate){
5987         
5988         if(!isUpdate){
5989             this.fireEvent("beforerowsinserted", this, rowIndex);
5990         }
5991             //var s = this.getScrollState();
5992         var row = this.renderRow(this.cm, this.store, rowIndex);
5993         // insert before rowIndex..
5994         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5995         
5996         var _this = this;
5997                 
5998         if(row.cellObjects.length){
5999             Roo.each(row.cellObjects, function(r){
6000                 _this.renderCellObject(r);
6001             })
6002         }
6003             
6004         if(!isUpdate){
6005             this.fireEvent("rowsinserted", this, rowIndex);
6006             //this.syncRowHeights(firstRow, lastRow);
6007             //this.stripeRows(firstRow);
6008             //this.layout();
6009         }
6010         
6011     },
6012     
6013     
6014     getRowDom : function(rowIndex)
6015     {
6016         var rows = this.el.select('tbody > tr', true).elements;
6017         
6018         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6019         
6020     },
6021     // returns the object tree for a tr..
6022   
6023     
6024     renderRow : function(cm, ds, rowIndex) 
6025     {
6026         
6027         var d = ds.getAt(rowIndex);
6028         
6029         var row = {
6030             tag : 'tr',
6031             cn : []
6032         };
6033             
6034         var cellObjects = [];
6035         
6036         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6037             var config = cm.config[i];
6038             
6039             var renderer = cm.getRenderer(i);
6040             var value = '';
6041             var id = false;
6042             
6043             if(typeof(renderer) !== 'undefined'){
6044                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6045             }
6046             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6047             // and are rendered into the cells after the row is rendered - using the id for the element.
6048             
6049             if(typeof(value) === 'object'){
6050                 id = Roo.id();
6051                 cellObjects.push({
6052                     container : id,
6053                     cfg : value 
6054                 })
6055             }
6056             
6057             var rowcfg = {
6058                 record: d,
6059                 rowIndex : rowIndex,
6060                 colIndex : i,
6061                 rowClass : ''
6062             }
6063
6064             this.fireEvent('rowclass', this, rowcfg);
6065             
6066             var td = {
6067                 tag: 'td',
6068                 cls : rowcfg.rowClass,
6069                 style: '',
6070                 html: (typeof(value) === 'object') ? '' : value
6071             };
6072             
6073             if (id) {
6074                 td.id = id;
6075             }
6076             
6077             if(typeof(config.colspan) != 'undefined'){
6078                 td.colspan = config.colspan;
6079             }
6080             
6081             if(typeof(config.hidden) != 'undefined' && config.hidden){
6082                 td.style += ' display:none;';
6083             }
6084             
6085             if(typeof(config.align) != 'undefined' && config.align.length){
6086                 td.style += ' text-align:' + config.align + ';';
6087             }
6088             
6089             if(typeof(config.width) != 'undefined'){
6090                 td.style += ' width:' +  config.width + 'px;';
6091             }
6092             
6093             if(typeof(config.cursor) != 'undefined'){
6094                 td.style += ' cursor:' +  config.cursor + ';';
6095             }
6096             
6097             if(typeof(config.cls) != 'undefined'){
6098                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6099             }
6100              
6101             row.cn.push(td);
6102            
6103         }
6104         
6105         row.cellObjects = cellObjects;
6106         
6107         return row;
6108           
6109     },
6110     
6111     
6112     
6113     onBeforeLoad : function()
6114     {
6115         //Roo.log('ds onBeforeLoad');
6116         
6117         //this.clear();
6118         
6119         //if(this.loadMask){
6120         //    this.maskEl.show();
6121         //}
6122     },
6123      /**
6124      * Remove all rows
6125      */
6126     clear : function()
6127     {
6128         this.el.select('tbody', true).first().dom.innerHTML = '';
6129     },
6130     /**
6131      * Show or hide a row.
6132      * @param {Number} rowIndex to show or hide
6133      * @param {Boolean} state hide
6134      */
6135     setRowVisibility : function(rowIndex, state)
6136     {
6137         var bt = this.mainBody.dom;
6138         
6139         var rows = this.el.select('tbody > tr', true).elements;
6140         
6141         if(typeof(rows[rowIndex]) == 'undefined'){
6142             return;
6143         }
6144         rows[rowIndex].dom.style.display = state ? '' : 'none';
6145     },
6146     
6147     
6148     getSelectionModel : function(){
6149         if(!this.selModel){
6150             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6151         }
6152         return this.selModel;
6153     },
6154     /*
6155      * Render the Roo.bootstrap object from renderder
6156      */
6157     renderCellObject : function(r)
6158     {
6159         var _this = this;
6160         
6161         var t = r.cfg.render(r.container);
6162         
6163         if(r.cfg.cn){
6164             Roo.each(r.cfg.cn, function(c){
6165                 var child = {
6166                     container: t.getChildContainer(),
6167                     cfg: c
6168                 }
6169                 _this.renderCellObject(child);
6170             })
6171         }
6172     },
6173     
6174     getRowIndex : function(row)
6175     {
6176         var rowIndex = -1;
6177         
6178         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6179             if(el != row){
6180                 return;
6181             }
6182             
6183             rowIndex = index;
6184         });
6185         
6186         return rowIndex;
6187     }
6188    
6189 });
6190
6191  
6192
6193  /*
6194  * - LGPL
6195  *
6196  * table cell
6197  * 
6198  */
6199
6200 /**
6201  * @class Roo.bootstrap.TableCell
6202  * @extends Roo.bootstrap.Component
6203  * Bootstrap TableCell class
6204  * @cfg {String} html cell contain text
6205  * @cfg {String} cls cell class
6206  * @cfg {String} tag cell tag (td|th) default td
6207  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6208  * @cfg {String} align Aligns the content in a cell
6209  * @cfg {String} axis Categorizes cells
6210  * @cfg {String} bgcolor Specifies the background color of a cell
6211  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6212  * @cfg {Number} colspan Specifies the number of columns a cell should span
6213  * @cfg {String} headers Specifies one or more header cells a cell is related to
6214  * @cfg {Number} height Sets the height of a cell
6215  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6216  * @cfg {Number} rowspan Sets the number of rows a cell should span
6217  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6218  * @cfg {String} valign Vertical aligns the content in a cell
6219  * @cfg {Number} width Specifies the width of a cell
6220  * 
6221  * @constructor
6222  * Create a new TableCell
6223  * @param {Object} config The config object
6224  */
6225
6226 Roo.bootstrap.TableCell = function(config){
6227     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6228 };
6229
6230 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6231     
6232     html: false,
6233     cls: false,
6234     tag: false,
6235     abbr: false,
6236     align: false,
6237     axis: false,
6238     bgcolor: false,
6239     charoff: false,
6240     colspan: false,
6241     headers: false,
6242     height: false,
6243     nowrap: false,
6244     rowspan: false,
6245     scope: false,
6246     valign: false,
6247     width: false,
6248     
6249     
6250     getAutoCreate : function(){
6251         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6252         
6253         cfg = {
6254             tag: 'td'
6255         }
6256         
6257         if(this.tag){
6258             cfg.tag = this.tag;
6259         }
6260         
6261         if (this.html) {
6262             cfg.html=this.html
6263         }
6264         if (this.cls) {
6265             cfg.cls=this.cls
6266         }
6267         if (this.abbr) {
6268             cfg.abbr=this.abbr
6269         }
6270         if (this.align) {
6271             cfg.align=this.align
6272         }
6273         if (this.axis) {
6274             cfg.axis=this.axis
6275         }
6276         if (this.bgcolor) {
6277             cfg.bgcolor=this.bgcolor
6278         }
6279         if (this.charoff) {
6280             cfg.charoff=this.charoff
6281         }
6282         if (this.colspan) {
6283             cfg.colspan=this.colspan
6284         }
6285         if (this.headers) {
6286             cfg.headers=this.headers
6287         }
6288         if (this.height) {
6289             cfg.height=this.height
6290         }
6291         if (this.nowrap) {
6292             cfg.nowrap=this.nowrap
6293         }
6294         if (this.rowspan) {
6295             cfg.rowspan=this.rowspan
6296         }
6297         if (this.scope) {
6298             cfg.scope=this.scope
6299         }
6300         if (this.valign) {
6301             cfg.valign=this.valign
6302         }
6303         if (this.width) {
6304             cfg.width=this.width
6305         }
6306         
6307         
6308         return cfg;
6309     }
6310    
6311 });
6312
6313  
6314
6315  /*
6316  * - LGPL
6317  *
6318  * table row
6319  * 
6320  */
6321
6322 /**
6323  * @class Roo.bootstrap.TableRow
6324  * @extends Roo.bootstrap.Component
6325  * Bootstrap TableRow class
6326  * @cfg {String} cls row class
6327  * @cfg {String} align Aligns the content in a table row
6328  * @cfg {String} bgcolor Specifies a background color for a table row
6329  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6330  * @cfg {String} valign Vertical aligns the content in a table row
6331  * 
6332  * @constructor
6333  * Create a new TableRow
6334  * @param {Object} config The config object
6335  */
6336
6337 Roo.bootstrap.TableRow = function(config){
6338     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6339 };
6340
6341 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6342     
6343     cls: false,
6344     align: false,
6345     bgcolor: false,
6346     charoff: false,
6347     valign: false,
6348     
6349     getAutoCreate : function(){
6350         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6351         
6352         cfg = {
6353             tag: 'tr'
6354         }
6355             
6356         if(this.cls){
6357             cfg.cls = this.cls;
6358         }
6359         if(this.align){
6360             cfg.align = this.align;
6361         }
6362         if(this.bgcolor){
6363             cfg.bgcolor = this.bgcolor;
6364         }
6365         if(this.charoff){
6366             cfg.charoff = this.charoff;
6367         }
6368         if(this.valign){
6369             cfg.valign = this.valign;
6370         }
6371         
6372         return cfg;
6373     }
6374    
6375 });
6376
6377  
6378
6379  /*
6380  * - LGPL
6381  *
6382  * table body
6383  * 
6384  */
6385
6386 /**
6387  * @class Roo.bootstrap.TableBody
6388  * @extends Roo.bootstrap.Component
6389  * Bootstrap TableBody class
6390  * @cfg {String} cls element class
6391  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6392  * @cfg {String} align Aligns the content inside the element
6393  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6394  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6395  * 
6396  * @constructor
6397  * Create a new TableBody
6398  * @param {Object} config The config object
6399  */
6400
6401 Roo.bootstrap.TableBody = function(config){
6402     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6403 };
6404
6405 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6406     
6407     cls: false,
6408     tag: false,
6409     align: false,
6410     charoff: false,
6411     valign: false,
6412     
6413     getAutoCreate : function(){
6414         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6415         
6416         cfg = {
6417             tag: 'tbody'
6418         }
6419             
6420         if (this.cls) {
6421             cfg.cls=this.cls
6422         }
6423         if(this.tag){
6424             cfg.tag = this.tag;
6425         }
6426         
6427         if(this.align){
6428             cfg.align = this.align;
6429         }
6430         if(this.charoff){
6431             cfg.charoff = this.charoff;
6432         }
6433         if(this.valign){
6434             cfg.valign = this.valign;
6435         }
6436         
6437         return cfg;
6438     }
6439     
6440     
6441 //    initEvents : function()
6442 //    {
6443 //        
6444 //        if(!this.store){
6445 //            return;
6446 //        }
6447 //        
6448 //        this.store = Roo.factory(this.store, Roo.data);
6449 //        this.store.on('load', this.onLoad, this);
6450 //        
6451 //        this.store.load();
6452 //        
6453 //    },
6454 //    
6455 //    onLoad: function () 
6456 //    {   
6457 //        this.fireEvent('load', this);
6458 //    }
6459 //    
6460 //   
6461 });
6462
6463  
6464
6465  /*
6466  * Based on:
6467  * Ext JS Library 1.1.1
6468  * Copyright(c) 2006-2007, Ext JS, LLC.
6469  *
6470  * Originally Released Under LGPL - original licence link has changed is not relivant.
6471  *
6472  * Fork - LGPL
6473  * <script type="text/javascript">
6474  */
6475
6476 // as we use this in bootstrap.
6477 Roo.namespace('Roo.form');
6478  /**
6479  * @class Roo.form.Action
6480  * Internal Class used to handle form actions
6481  * @constructor
6482  * @param {Roo.form.BasicForm} el The form element or its id
6483  * @param {Object} config Configuration options
6484  */
6485
6486  
6487  
6488 // define the action interface
6489 Roo.form.Action = function(form, options){
6490     this.form = form;
6491     this.options = options || {};
6492 };
6493 /**
6494  * Client Validation Failed
6495  * @const 
6496  */
6497 Roo.form.Action.CLIENT_INVALID = 'client';
6498 /**
6499  * Server Validation Failed
6500  * @const 
6501  */
6502 Roo.form.Action.SERVER_INVALID = 'server';
6503  /**
6504  * Connect to Server Failed
6505  * @const 
6506  */
6507 Roo.form.Action.CONNECT_FAILURE = 'connect';
6508 /**
6509  * Reading Data from Server Failed
6510  * @const 
6511  */
6512 Roo.form.Action.LOAD_FAILURE = 'load';
6513
6514 Roo.form.Action.prototype = {
6515     type : 'default',
6516     failureType : undefined,
6517     response : undefined,
6518     result : undefined,
6519
6520     // interface method
6521     run : function(options){
6522
6523     },
6524
6525     // interface method
6526     success : function(response){
6527
6528     },
6529
6530     // interface method
6531     handleResponse : function(response){
6532
6533     },
6534
6535     // default connection failure
6536     failure : function(response){
6537         
6538         this.response = response;
6539         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6540         this.form.afterAction(this, false);
6541     },
6542
6543     processResponse : function(response){
6544         this.response = response;
6545         if(!response.responseText){
6546             return true;
6547         }
6548         this.result = this.handleResponse(response);
6549         return this.result;
6550     },
6551
6552     // utility functions used internally
6553     getUrl : function(appendParams){
6554         var url = this.options.url || this.form.url || this.form.el.dom.action;
6555         if(appendParams){
6556             var p = this.getParams();
6557             if(p){
6558                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6559             }
6560         }
6561         return url;
6562     },
6563
6564     getMethod : function(){
6565         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6566     },
6567
6568     getParams : function(){
6569         var bp = this.form.baseParams;
6570         var p = this.options.params;
6571         if(p){
6572             if(typeof p == "object"){
6573                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6574             }else if(typeof p == 'string' && bp){
6575                 p += '&' + Roo.urlEncode(bp);
6576             }
6577         }else if(bp){
6578             p = Roo.urlEncode(bp);
6579         }
6580         return p;
6581     },
6582
6583     createCallback : function(){
6584         return {
6585             success: this.success,
6586             failure: this.failure,
6587             scope: this,
6588             timeout: (this.form.timeout*1000),
6589             upload: this.form.fileUpload ? this.success : undefined
6590         };
6591     }
6592 };
6593
6594 Roo.form.Action.Submit = function(form, options){
6595     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6596 };
6597
6598 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6599     type : 'submit',
6600
6601     haveProgress : false,
6602     uploadComplete : false,
6603     
6604     // uploadProgress indicator.
6605     uploadProgress : function()
6606     {
6607         if (!this.form.progressUrl) {
6608             return;
6609         }
6610         
6611         if (!this.haveProgress) {
6612             Roo.MessageBox.progress("Uploading", "Uploading");
6613         }
6614         if (this.uploadComplete) {
6615            Roo.MessageBox.hide();
6616            return;
6617         }
6618         
6619         this.haveProgress = true;
6620    
6621         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6622         
6623         var c = new Roo.data.Connection();
6624         c.request({
6625             url : this.form.progressUrl,
6626             params: {
6627                 id : uid
6628             },
6629             method: 'GET',
6630             success : function(req){
6631                //console.log(data);
6632                 var rdata = false;
6633                 var edata;
6634                 try  {
6635                    rdata = Roo.decode(req.responseText)
6636                 } catch (e) {
6637                     Roo.log("Invalid data from server..");
6638                     Roo.log(edata);
6639                     return;
6640                 }
6641                 if (!rdata || !rdata.success) {
6642                     Roo.log(rdata);
6643                     Roo.MessageBox.alert(Roo.encode(rdata));
6644                     return;
6645                 }
6646                 var data = rdata.data;
6647                 
6648                 if (this.uploadComplete) {
6649                    Roo.MessageBox.hide();
6650                    return;
6651                 }
6652                    
6653                 if (data){
6654                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6655                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6656                     );
6657                 }
6658                 this.uploadProgress.defer(2000,this);
6659             },
6660        
6661             failure: function(data) {
6662                 Roo.log('progress url failed ');
6663                 Roo.log(data);
6664             },
6665             scope : this
6666         });
6667            
6668     },
6669     
6670     
6671     run : function()
6672     {
6673         // run get Values on the form, so it syncs any secondary forms.
6674         this.form.getValues();
6675         
6676         var o = this.options;
6677         var method = this.getMethod();
6678         var isPost = method == 'POST';
6679         if(o.clientValidation === false || this.form.isValid()){
6680             
6681             if (this.form.progressUrl) {
6682                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6683                     (new Date() * 1) + '' + Math.random());
6684                     
6685             } 
6686             
6687             
6688             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6689                 form:this.form.el.dom,
6690                 url:this.getUrl(!isPost),
6691                 method: method,
6692                 params:isPost ? this.getParams() : null,
6693                 isUpload: this.form.fileUpload
6694             }));
6695             
6696             this.uploadProgress();
6697
6698         }else if (o.clientValidation !== false){ // client validation failed
6699             this.failureType = Roo.form.Action.CLIENT_INVALID;
6700             this.form.afterAction(this, false);
6701         }
6702     },
6703
6704     success : function(response)
6705     {
6706         this.uploadComplete= true;
6707         if (this.haveProgress) {
6708             Roo.MessageBox.hide();
6709         }
6710         
6711         
6712         var result = this.processResponse(response);
6713         if(result === true || result.success){
6714             this.form.afterAction(this, true);
6715             return;
6716         }
6717         if(result.errors){
6718             this.form.markInvalid(result.errors);
6719             this.failureType = Roo.form.Action.SERVER_INVALID;
6720         }
6721         this.form.afterAction(this, false);
6722     },
6723     failure : function(response)
6724     {
6725         this.uploadComplete= true;
6726         if (this.haveProgress) {
6727             Roo.MessageBox.hide();
6728         }
6729         
6730         this.response = response;
6731         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6732         this.form.afterAction(this, false);
6733     },
6734     
6735     handleResponse : function(response){
6736         if(this.form.errorReader){
6737             var rs = this.form.errorReader.read(response);
6738             var errors = [];
6739             if(rs.records){
6740                 for(var i = 0, len = rs.records.length; i < len; i++) {
6741                     var r = rs.records[i];
6742                     errors[i] = r.data;
6743                 }
6744             }
6745             if(errors.length < 1){
6746                 errors = null;
6747             }
6748             return {
6749                 success : rs.success,
6750                 errors : errors
6751             };
6752         }
6753         var ret = false;
6754         try {
6755             ret = Roo.decode(response.responseText);
6756         } catch (e) {
6757             ret = {
6758                 success: false,
6759                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6760                 errors : []
6761             };
6762         }
6763         return ret;
6764         
6765     }
6766 });
6767
6768
6769 Roo.form.Action.Load = function(form, options){
6770     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6771     this.reader = this.form.reader;
6772 };
6773
6774 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6775     type : 'load',
6776
6777     run : function(){
6778         
6779         Roo.Ajax.request(Roo.apply(
6780                 this.createCallback(), {
6781                     method:this.getMethod(),
6782                     url:this.getUrl(false),
6783                     params:this.getParams()
6784         }));
6785     },
6786
6787     success : function(response){
6788         
6789         var result = this.processResponse(response);
6790         if(result === true || !result.success || !result.data){
6791             this.failureType = Roo.form.Action.LOAD_FAILURE;
6792             this.form.afterAction(this, false);
6793             return;
6794         }
6795         this.form.clearInvalid();
6796         this.form.setValues(result.data);
6797         this.form.afterAction(this, true);
6798     },
6799
6800     handleResponse : function(response){
6801         if(this.form.reader){
6802             var rs = this.form.reader.read(response);
6803             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6804             return {
6805                 success : rs.success,
6806                 data : data
6807             };
6808         }
6809         return Roo.decode(response.responseText);
6810     }
6811 });
6812
6813 Roo.form.Action.ACTION_TYPES = {
6814     'load' : Roo.form.Action.Load,
6815     'submit' : Roo.form.Action.Submit
6816 };/*
6817  * - LGPL
6818  *
6819  * form
6820  * 
6821  */
6822
6823 /**
6824  * @class Roo.bootstrap.Form
6825  * @extends Roo.bootstrap.Component
6826  * Bootstrap Form class
6827  * @cfg {String} method  GET | POST (default POST)
6828  * @cfg {String} labelAlign top | left (default top)
6829  * @cfg {String} align left  | right - for navbars
6830  * @cfg {Boolean} loadMask load mask when submit (default true)
6831
6832  * 
6833  * @constructor
6834  * Create a new Form
6835  * @param {Object} config The config object
6836  */
6837
6838
6839 Roo.bootstrap.Form = function(config){
6840     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6841     this.addEvents({
6842         /**
6843          * @event clientvalidation
6844          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6845          * @param {Form} this
6846          * @param {Boolean} valid true if the form has passed client-side validation
6847          */
6848         clientvalidation: true,
6849         /**
6850          * @event beforeaction
6851          * Fires before any action is performed. Return false to cancel the action.
6852          * @param {Form} this
6853          * @param {Action} action The action to be performed
6854          */
6855         beforeaction: true,
6856         /**
6857          * @event actionfailed
6858          * Fires when an action fails.
6859          * @param {Form} this
6860          * @param {Action} action The action that failed
6861          */
6862         actionfailed : true,
6863         /**
6864          * @event actioncomplete
6865          * Fires when an action is completed.
6866          * @param {Form} this
6867          * @param {Action} action The action that completed
6868          */
6869         actioncomplete : true
6870     });
6871     
6872 };
6873
6874 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6875       
6876      /**
6877      * @cfg {String} method
6878      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6879      */
6880     method : 'POST',
6881     /**
6882      * @cfg {String} url
6883      * The URL to use for form actions if one isn't supplied in the action options.
6884      */
6885     /**
6886      * @cfg {Boolean} fileUpload
6887      * Set to true if this form is a file upload.
6888      */
6889      
6890     /**
6891      * @cfg {Object} baseParams
6892      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6893      */
6894       
6895     /**
6896      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6897      */
6898     timeout: 30,
6899     /**
6900      * @cfg {Sting} align (left|right) for navbar forms
6901      */
6902     align : 'left',
6903
6904     // private
6905     activeAction : null,
6906  
6907     /**
6908      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6909      * element by passing it or its id or mask the form itself by passing in true.
6910      * @type Mixed
6911      */
6912     waitMsgTarget : false,
6913     
6914     loadMask : true,
6915     
6916     getAutoCreate : function(){
6917         
6918         var cfg = {
6919             tag: 'form',
6920             method : this.method || 'POST',
6921             id : this.id || Roo.id(),
6922             cls : ''
6923         }
6924         if (this.parent().xtype.match(/^Nav/)) {
6925             cfg.cls = 'navbar-form navbar-' + this.align;
6926             
6927         }
6928         
6929         if (this.labelAlign == 'left' ) {
6930             cfg.cls += ' form-horizontal';
6931         }
6932         
6933         
6934         return cfg;
6935     },
6936     initEvents : function()
6937     {
6938         this.el.on('submit', this.onSubmit, this);
6939         // this was added as random key presses on the form where triggering form submit.
6940         this.el.on('keypress', function(e) {
6941             if (e.getCharCode() != 13) {
6942                 return true;
6943             }
6944             // we might need to allow it for textareas.. and some other items.
6945             // check e.getTarget().
6946             
6947             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6948                 return true;
6949             }
6950         
6951             Roo.log("keypress blocked");
6952             
6953             e.preventDefault();
6954             return false;
6955         });
6956         
6957     },
6958     // private
6959     onSubmit : function(e){
6960         e.stopEvent();
6961     },
6962     
6963      /**
6964      * Returns true if client-side validation on the form is successful.
6965      * @return Boolean
6966      */
6967     isValid : function(){
6968         var items = this.getItems();
6969         var valid = true;
6970         items.each(function(f){
6971            if(!f.validate()){
6972                valid = false;
6973                
6974            }
6975         });
6976         return valid;
6977     },
6978     /**
6979      * Returns true if any fields in this form have changed since their original load.
6980      * @return Boolean
6981      */
6982     isDirty : function(){
6983         var dirty = false;
6984         var items = this.getItems();
6985         items.each(function(f){
6986            if(f.isDirty()){
6987                dirty = true;
6988                return false;
6989            }
6990            return true;
6991         });
6992         return dirty;
6993     },
6994      /**
6995      * Performs a predefined action (submit or load) or custom actions you define on this form.
6996      * @param {String} actionName The name of the action type
6997      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6998      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6999      * accept other config options):
7000      * <pre>
7001 Property          Type             Description
7002 ----------------  ---------------  ----------------------------------------------------------------------------------
7003 url               String           The url for the action (defaults to the form's url)
7004 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7005 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7006 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7007                                    validate the form on the client (defaults to false)
7008      * </pre>
7009      * @return {BasicForm} this
7010      */
7011     doAction : function(action, options){
7012         if(typeof action == 'string'){
7013             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7014         }
7015         if(this.fireEvent('beforeaction', this, action) !== false){
7016             this.beforeAction(action);
7017             action.run.defer(100, action);
7018         }
7019         return this;
7020     },
7021     
7022     // private
7023     beforeAction : function(action){
7024         var o = action.options;
7025         
7026         if(this.loadMask){
7027             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7028         }
7029         // not really supported yet.. ??
7030         
7031         //if(this.waitMsgTarget === true){
7032         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7033         //}else if(this.waitMsgTarget){
7034         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7035         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7036         //}else {
7037         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7038        // }
7039          
7040     },
7041
7042     // private
7043     afterAction : function(action, success){
7044         this.activeAction = null;
7045         var o = action.options;
7046         
7047         //if(this.waitMsgTarget === true){
7048             this.el.unmask();
7049         //}else if(this.waitMsgTarget){
7050         //    this.waitMsgTarget.unmask();
7051         //}else{
7052         //    Roo.MessageBox.updateProgress(1);
7053         //    Roo.MessageBox.hide();
7054        // }
7055         // 
7056         if(success){
7057             if(o.reset){
7058                 this.reset();
7059             }
7060             Roo.callback(o.success, o.scope, [this, action]);
7061             this.fireEvent('actioncomplete', this, action);
7062             
7063         }else{
7064             
7065             // failure condition..
7066             // we have a scenario where updates need confirming.
7067             // eg. if a locking scenario exists..
7068             // we look for { errors : { needs_confirm : true }} in the response.
7069             if (
7070                 (typeof(action.result) != 'undefined')  &&
7071                 (typeof(action.result.errors) != 'undefined')  &&
7072                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7073            ){
7074                 var _t = this;
7075                 Roo.log("not supported yet");
7076                  /*
7077                 
7078                 Roo.MessageBox.confirm(
7079                     "Change requires confirmation",
7080                     action.result.errorMsg,
7081                     function(r) {
7082                         if (r != 'yes') {
7083                             return;
7084                         }
7085                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7086                     }
7087                     
7088                 );
7089                 */
7090                 
7091                 
7092                 return;
7093             }
7094             
7095             Roo.callback(o.failure, o.scope, [this, action]);
7096             // show an error message if no failed handler is set..
7097             if (!this.hasListener('actionfailed')) {
7098                 Roo.log("need to add dialog support");
7099                 /*
7100                 Roo.MessageBox.alert("Error",
7101                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7102                         action.result.errorMsg :
7103                         "Saving Failed, please check your entries or try again"
7104                 );
7105                 */
7106             }
7107             
7108             this.fireEvent('actionfailed', this, action);
7109         }
7110         
7111     },
7112     /**
7113      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7114      * @param {String} id The value to search for
7115      * @return Field
7116      */
7117     findField : function(id){
7118         var items = this.getItems();
7119         var field = items.get(id);
7120         if(!field){
7121              items.each(function(f){
7122                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7123                     field = f;
7124                     return false;
7125                 }
7126                 return true;
7127             });
7128         }
7129         return field || null;
7130     },
7131      /**
7132      * Mark fields in this form invalid in bulk.
7133      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7134      * @return {BasicForm} this
7135      */
7136     markInvalid : function(errors){
7137         if(errors instanceof Array){
7138             for(var i = 0, len = errors.length; i < len; i++){
7139                 var fieldError = errors[i];
7140                 var f = this.findField(fieldError.id);
7141                 if(f){
7142                     f.markInvalid(fieldError.msg);
7143                 }
7144             }
7145         }else{
7146             var field, id;
7147             for(id in errors){
7148                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7149                     field.markInvalid(errors[id]);
7150                 }
7151             }
7152         }
7153         //Roo.each(this.childForms || [], function (f) {
7154         //    f.markInvalid(errors);
7155         //});
7156         
7157         return this;
7158     },
7159
7160     /**
7161      * Set values for fields in this form in bulk.
7162      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7163      * @return {BasicForm} this
7164      */
7165     setValues : function(values){
7166         if(values instanceof Array){ // array of objects
7167             for(var i = 0, len = values.length; i < len; i++){
7168                 var v = values[i];
7169                 var f = this.findField(v.id);
7170                 if(f){
7171                     f.setValue(v.value);
7172                     if(this.trackResetOnLoad){
7173                         f.originalValue = f.getValue();
7174                     }
7175                 }
7176             }
7177         }else{ // object hash
7178             var field, id;
7179             for(id in values){
7180                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7181                     
7182                     if (field.setFromData && 
7183                         field.valueField && 
7184                         field.displayField &&
7185                         // combos' with local stores can 
7186                         // be queried via setValue()
7187                         // to set their value..
7188                         (field.store && !field.store.isLocal)
7189                         ) {
7190                         // it's a combo
7191                         var sd = { };
7192                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7193                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7194                         field.setFromData(sd);
7195                         
7196                     } else {
7197                         field.setValue(values[id]);
7198                     }
7199                     
7200                     
7201                     if(this.trackResetOnLoad){
7202                         field.originalValue = field.getValue();
7203                     }
7204                 }
7205             }
7206         }
7207          
7208         //Roo.each(this.childForms || [], function (f) {
7209         //    f.setValues(values);
7210         //});
7211                 
7212         return this;
7213     },
7214
7215     /**
7216      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7217      * they are returned as an array.
7218      * @param {Boolean} asString
7219      * @return {Object}
7220      */
7221     getValues : function(asString){
7222         //if (this.childForms) {
7223             // copy values from the child forms
7224         //    Roo.each(this.childForms, function (f) {
7225         //        this.setValues(f.getValues());
7226         //    }, this);
7227         //}
7228         
7229         
7230         
7231         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7232         if(asString === true){
7233             return fs;
7234         }
7235         return Roo.urlDecode(fs);
7236     },
7237     
7238     /**
7239      * Returns the fields in this form as an object with key/value pairs. 
7240      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7241      * @return {Object}
7242      */
7243     getFieldValues : function(with_hidden)
7244     {
7245         var items = this.getItems();
7246         var ret = {};
7247         items.each(function(f){
7248             if (!f.getName()) {
7249                 return;
7250             }
7251             var v = f.getValue();
7252             if (f.inputType =='radio') {
7253                 if (typeof(ret[f.getName()]) == 'undefined') {
7254                     ret[f.getName()] = ''; // empty..
7255                 }
7256                 
7257                 if (!f.el.dom.checked) {
7258                     return;
7259                     
7260                 }
7261                 v = f.el.dom.value;
7262                 
7263             }
7264             
7265             // not sure if this supported any more..
7266             if ((typeof(v) == 'object') && f.getRawValue) {
7267                 v = f.getRawValue() ; // dates..
7268             }
7269             // combo boxes where name != hiddenName...
7270             if (f.name != f.getName()) {
7271                 ret[f.name] = f.getRawValue();
7272             }
7273             ret[f.getName()] = v;
7274         });
7275         
7276         return ret;
7277     },
7278
7279     /**
7280      * Clears all invalid messages in this form.
7281      * @return {BasicForm} this
7282      */
7283     clearInvalid : function(){
7284         var items = this.getItems();
7285         
7286         items.each(function(f){
7287            f.clearInvalid();
7288         });
7289         
7290         
7291         
7292         return this;
7293     },
7294
7295     /**
7296      * Resets this form.
7297      * @return {BasicForm} this
7298      */
7299     reset : function(){
7300         var items = this.getItems();
7301         items.each(function(f){
7302             f.reset();
7303         });
7304         
7305         Roo.each(this.childForms || [], function (f) {
7306             f.reset();
7307         });
7308        
7309         
7310         return this;
7311     },
7312     getItems : function()
7313     {
7314         var r=new Roo.util.MixedCollection(false, function(o){
7315             return o.id || (o.id = Roo.id());
7316         });
7317         var iter = function(el) {
7318             if (el.inputEl) {
7319                 r.add(el);
7320             }
7321             if (!el.items) {
7322                 return;
7323             }
7324             Roo.each(el.items,function(e) {
7325                 iter(e);
7326             });
7327             
7328             
7329         };
7330         
7331         iter(this);
7332         return r;
7333         
7334         
7335         
7336         
7337     }
7338     
7339 });
7340
7341  
7342 /*
7343  * Based on:
7344  * Ext JS Library 1.1.1
7345  * Copyright(c) 2006-2007, Ext JS, LLC.
7346  *
7347  * Originally Released Under LGPL - original licence link has changed is not relivant.
7348  *
7349  * Fork - LGPL
7350  * <script type="text/javascript">
7351  */
7352 /**
7353  * @class Roo.form.VTypes
7354  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7355  * @singleton
7356  */
7357 Roo.form.VTypes = function(){
7358     // closure these in so they are only created once.
7359     var alpha = /^[a-zA-Z_]+$/;
7360     var alphanum = /^[a-zA-Z0-9_]+$/;
7361     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7362     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7363
7364     // All these messages and functions are configurable
7365     return {
7366         /**
7367          * The function used to validate email addresses
7368          * @param {String} value The email address
7369          */
7370         'email' : function(v){
7371             return email.test(v);
7372         },
7373         /**
7374          * The error text to display when the email validation function returns false
7375          * @type String
7376          */
7377         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7378         /**
7379          * The keystroke filter mask to be applied on email input
7380          * @type RegExp
7381          */
7382         'emailMask' : /[a-z0-9_\.\-@]/i,
7383
7384         /**
7385          * The function used to validate URLs
7386          * @param {String} value The URL
7387          */
7388         'url' : function(v){
7389             return url.test(v);
7390         },
7391         /**
7392          * The error text to display when the url validation function returns false
7393          * @type String
7394          */
7395         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7396         
7397         /**
7398          * The function used to validate alpha values
7399          * @param {String} value The value
7400          */
7401         'alpha' : function(v){
7402             return alpha.test(v);
7403         },
7404         /**
7405          * The error text to display when the alpha validation function returns false
7406          * @type String
7407          */
7408         'alphaText' : 'This field should only contain letters and _',
7409         /**
7410          * The keystroke filter mask to be applied on alpha input
7411          * @type RegExp
7412          */
7413         'alphaMask' : /[a-z_]/i,
7414
7415         /**
7416          * The function used to validate alphanumeric values
7417          * @param {String} value The value
7418          */
7419         'alphanum' : function(v){
7420             return alphanum.test(v);
7421         },
7422         /**
7423          * The error text to display when the alphanumeric validation function returns false
7424          * @type String
7425          */
7426         'alphanumText' : 'This field should only contain letters, numbers and _',
7427         /**
7428          * The keystroke filter mask to be applied on alphanumeric input
7429          * @type RegExp
7430          */
7431         'alphanumMask' : /[a-z0-9_]/i
7432     };
7433 }();/*
7434  * - LGPL
7435  *
7436  * Input
7437  * 
7438  */
7439
7440 /**
7441  * @class Roo.bootstrap.Input
7442  * @extends Roo.bootstrap.Component
7443  * Bootstrap Input class
7444  * @cfg {Boolean} disabled is it disabled
7445  * @cfg {String} fieldLabel - the label associated
7446  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7447  * @cfg {String} name name of the input
7448  * @cfg {string} fieldLabel - the label associated
7449  * @cfg {string}  inputType - input / file submit ...
7450  * @cfg {string} placeholder - placeholder to put in text.
7451  * @cfg {string}  before - input group add on before
7452  * @cfg {string} after - input group add on after
7453  * @cfg {string} size - (lg|sm) or leave empty..
7454  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7455  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7456  * @cfg {Number} md colspan out of 12 for computer-sized screens
7457  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7458  * @cfg {string} value default value of the input
7459  * @cfg {Number} labelWidth set the width of label (0-12)
7460  * @cfg {String} labelAlign (top|left)
7461  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7462  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7463
7464  * @cfg {String} align (left|center|right) Default left
7465  * 
7466  * 
7467  * 
7468  * @constructor
7469  * Create a new Input
7470  * @param {Object} config The config object
7471  */
7472
7473 Roo.bootstrap.Input = function(config){
7474     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7475    
7476         this.addEvents({
7477             /**
7478              * @event focus
7479              * Fires when this field receives input focus.
7480              * @param {Roo.form.Field} this
7481              */
7482             focus : true,
7483             /**
7484              * @event blur
7485              * Fires when this field loses input focus.
7486              * @param {Roo.form.Field} this
7487              */
7488             blur : true,
7489             /**
7490              * @event specialkey
7491              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7492              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7493              * @param {Roo.form.Field} this
7494              * @param {Roo.EventObject} e The event object
7495              */
7496             specialkey : true,
7497             /**
7498              * @event change
7499              * Fires just before the field blurs if the field value has changed.
7500              * @param {Roo.form.Field} this
7501              * @param {Mixed} newValue The new value
7502              * @param {Mixed} oldValue The original value
7503              */
7504             change : true,
7505             /**
7506              * @event invalid
7507              * Fires after the field has been marked as invalid.
7508              * @param {Roo.form.Field} this
7509              * @param {String} msg The validation message
7510              */
7511             invalid : true,
7512             /**
7513              * @event valid
7514              * Fires after the field has been validated with no errors.
7515              * @param {Roo.form.Field} this
7516              */
7517             valid : true,
7518              /**
7519              * @event keyup
7520              * Fires after the key up
7521              * @param {Roo.form.Field} this
7522              * @param {Roo.EventObject}  e The event Object
7523              */
7524             keyup : true
7525         });
7526 };
7527
7528 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7529      /**
7530      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7531       automatic validation (defaults to "keyup").
7532      */
7533     validationEvent : "keyup",
7534      /**
7535      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7536      */
7537     validateOnBlur : true,
7538     /**
7539      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7540      */
7541     validationDelay : 250,
7542      /**
7543      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7544      */
7545     focusClass : "x-form-focus",  // not needed???
7546     
7547        
7548     /**
7549      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7550      */
7551     invalidClass : "has-warning",
7552     
7553     /**
7554      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7555      */
7556     validClass : "has-success",
7557     
7558     /**
7559      * @cfg {Boolean} hasFeedback (true|false) default true
7560      */
7561     hasFeedback : true,
7562     
7563     /**
7564      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7565      */
7566     invalidFeedbackClass : "glyphicon-warning-sign",
7567     
7568     /**
7569      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7570      */
7571     validFeedbackClass : "glyphicon-ok",
7572     
7573     /**
7574      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7575      */
7576     selectOnFocus : false,
7577     
7578      /**
7579      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7580      */
7581     maskRe : null,
7582        /**
7583      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7584      */
7585     vtype : null,
7586     
7587       /**
7588      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7589      */
7590     disableKeyFilter : false,
7591     
7592        /**
7593      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7594      */
7595     disabled : false,
7596      /**
7597      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7598      */
7599     allowBlank : true,
7600     /**
7601      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7602      */
7603     blankText : "This field is required",
7604     
7605      /**
7606      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7607      */
7608     minLength : 0,
7609     /**
7610      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7611      */
7612     maxLength : Number.MAX_VALUE,
7613     /**
7614      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7615      */
7616     minLengthText : "The minimum length for this field is {0}",
7617     /**
7618      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7619      */
7620     maxLengthText : "The maximum length for this field is {0}",
7621   
7622     
7623     /**
7624      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7625      * If available, this function will be called only after the basic validators all return true, and will be passed the
7626      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7627      */
7628     validator : null,
7629     /**
7630      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7631      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7632      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7633      */
7634     regex : null,
7635     /**
7636      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7637      */
7638     regexText : "",
7639     
7640     autocomplete: false,
7641     
7642     
7643     fieldLabel : '',
7644     inputType : 'text',
7645     
7646     name : false,
7647     placeholder: false,
7648     before : false,
7649     after : false,
7650     size : false,
7651     hasFocus : false,
7652     preventMark: false,
7653     isFormField : true,
7654     value : '',
7655     labelWidth : 2,
7656     labelAlign : false,
7657     readOnly : false,
7658     align : false,
7659     formatedValue : false,
7660     
7661     parentLabelAlign : function()
7662     {
7663         var parent = this;
7664         while (parent.parent()) {
7665             parent = parent.parent();
7666             if (typeof(parent.labelAlign) !='undefined') {
7667                 return parent.labelAlign;
7668             }
7669         }
7670         return 'left';
7671         
7672     },
7673     
7674     getAutoCreate : function(){
7675         
7676         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7677         
7678         var id = Roo.id();
7679         
7680         var cfg = {};
7681         
7682         if(this.inputType != 'hidden'){
7683             cfg.cls = 'form-group' //input-group
7684         }
7685         
7686         var input =  {
7687             tag: 'input',
7688             id : id,
7689             type : this.inputType,
7690             value : this.value,
7691             cls : 'form-control',
7692             placeholder : this.placeholder || '',
7693             autocomplete : this.autocomplete || 'new-password'
7694         };
7695         
7696         
7697         if(this.align){
7698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7699         }
7700         
7701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7702             input.maxLength = this.maxLength;
7703         }
7704         
7705         if (this.disabled) {
7706             input.disabled=true;
7707         }
7708         
7709         if (this.readOnly) {
7710             input.readonly=true;
7711         }
7712         
7713         if (this.name) {
7714             input.name = this.name;
7715         }
7716         if (this.size) {
7717             input.cls += ' input-' + this.size;
7718         }
7719         var settings=this;
7720         ['xs','sm','md','lg'].map(function(size){
7721             if (settings[size]) {
7722                 cfg.cls += ' col-' + size + '-' + settings[size];
7723             }
7724         });
7725         
7726         var inputblock = input;
7727         
7728         var feedback = {
7729             tag: 'span',
7730             cls: 'glyphicon form-control-feedback'
7731         };
7732             
7733         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7734             
7735             inputblock = {
7736                 cls : 'has-feedback',
7737                 cn :  [
7738                     input,
7739                     feedback
7740                 ] 
7741             };  
7742         }
7743         
7744         if (this.before || this.after) {
7745             
7746             inputblock = {
7747                 cls : 'input-group',
7748                 cn :  [] 
7749             };
7750             
7751             if (this.before && typeof(this.before) == 'string') {
7752                 
7753                 inputblock.cn.push({
7754                     tag :'span',
7755                     cls : 'roo-input-before input-group-addon',
7756                     html : this.before
7757                 });
7758             }
7759             if (this.before && typeof(this.before) == 'object') {
7760                 this.before = Roo.factory(this.before);
7761                 Roo.log(this.before);
7762                 inputblock.cn.push({
7763                     tag :'span',
7764                     cls : 'roo-input-before input-group-' +
7765                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7766                 });
7767             }
7768             
7769             inputblock.cn.push(input);
7770             
7771             if (this.after && typeof(this.after) == 'string') {
7772                 inputblock.cn.push({
7773                     tag :'span',
7774                     cls : 'roo-input-after input-group-addon',
7775                     html : this.after
7776                 });
7777             }
7778             if (this.after && typeof(this.after) == 'object') {
7779                 this.after = Roo.factory(this.after);
7780                 Roo.log(this.after);
7781                 inputblock.cn.push({
7782                     tag :'span',
7783                     cls : 'roo-input-after input-group-' +
7784                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7785                 });
7786             }
7787             
7788             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7789                 inputblock.cls += ' has-feedback';
7790                 inputblock.cn.push(feedback);
7791             }
7792         };
7793         
7794         if (align ==='left' && this.fieldLabel.length) {
7795                 Roo.log("left and has label");
7796                 cfg.cn = [
7797                     
7798                     {
7799                         tag: 'label',
7800                         'for' :  id,
7801                         cls : 'control-label col-sm-' + this.labelWidth,
7802                         html : this.fieldLabel
7803                         
7804                     },
7805                     {
7806                         cls : "col-sm-" + (12 - this.labelWidth), 
7807                         cn: [
7808                             inputblock
7809                         ]
7810                     }
7811                     
7812                 ];
7813         } else if ( this.fieldLabel.length) {
7814                 Roo.log(" label");
7815                  cfg.cn = [
7816                    
7817                     {
7818                         tag: 'label',
7819                         //cls : 'input-group-addon',
7820                         html : this.fieldLabel
7821                         
7822                     },
7823                     
7824                     inputblock
7825                     
7826                 ];
7827
7828         } else {
7829             
7830                 Roo.log(" no label && no align");
7831                 cfg.cn = [
7832                     
7833                         inputblock
7834                     
7835                 ];
7836                 
7837                 
7838         };
7839         Roo.log('input-parentType: ' + this.parentType);
7840         
7841         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7842            cfg.cls += ' navbar-form';
7843            Roo.log(cfg);
7844         }
7845         
7846         return cfg;
7847         
7848     },
7849     /**
7850      * return the real input element.
7851      */
7852     inputEl: function ()
7853     {
7854         return this.el.select('input.form-control',true).first();
7855     },
7856     
7857     tooltipEl : function()
7858     {
7859         return this.inputEl();
7860     },
7861     
7862     setDisabled : function(v)
7863     {
7864         var i  = this.inputEl().dom;
7865         if (!v) {
7866             i.removeAttribute('disabled');
7867             return;
7868             
7869         }
7870         i.setAttribute('disabled','true');
7871     },
7872     initEvents : function()
7873     {
7874           
7875         this.inputEl().on("keydown" , this.fireKey,  this);
7876         this.inputEl().on("focus", this.onFocus,  this);
7877         this.inputEl().on("blur", this.onBlur,  this);
7878         
7879         this.inputEl().relayEvent('keyup', this);
7880
7881         // reference to original value for reset
7882         this.originalValue = this.getValue();
7883         //Roo.form.TextField.superclass.initEvents.call(this);
7884         if(this.validationEvent == 'keyup'){
7885             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7886             this.inputEl().on('keyup', this.filterValidation, this);
7887         }
7888         else if(this.validationEvent !== false){
7889             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7890         }
7891         
7892         if(this.selectOnFocus){
7893             this.on("focus", this.preFocus, this);
7894             
7895         }
7896         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7897             this.inputEl().on("keypress", this.filterKeys, this);
7898         }
7899        /* if(this.grow){
7900             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7901             this.el.on("click", this.autoSize,  this);
7902         }
7903         */
7904         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7905             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7906         }
7907         
7908         if (typeof(this.before) == 'object') {
7909             this.before.render(this.el.select('.roo-input-before',true).first());
7910         }
7911         if (typeof(this.after) == 'object') {
7912             this.after.render(this.el.select('.roo-input-after',true).first());
7913         }
7914         
7915         
7916     },
7917     filterValidation : function(e){
7918         if(!e.isNavKeyPress()){
7919             this.validationTask.delay(this.validationDelay);
7920         }
7921     },
7922      /**
7923      * Validates the field value
7924      * @return {Boolean} True if the value is valid, else false
7925      */
7926     validate : function(){
7927         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7928         if(this.disabled || this.validateValue(this.getRawValue())){
7929             this.markValid();
7930             return true;
7931         }
7932         
7933         this.markInvalid();
7934         return false;
7935     },
7936     
7937     
7938     /**
7939      * Validates a value according to the field's validation rules and marks the field as invalid
7940      * if the validation fails
7941      * @param {Mixed} value The value to validate
7942      * @return {Boolean} True if the value is valid, else false
7943      */
7944     validateValue : function(value){
7945         if(value.length < 1)  { // if it's blank
7946             if(this.allowBlank){
7947                 return true;
7948             }
7949             return false;
7950         }
7951         
7952         if(value.length < this.minLength){
7953             return false;
7954         }
7955         if(value.length > this.maxLength){
7956             return false;
7957         }
7958         if(this.vtype){
7959             var vt = Roo.form.VTypes;
7960             if(!vt[this.vtype](value, this)){
7961                 return false;
7962             }
7963         }
7964         if(typeof this.validator == "function"){
7965             var msg = this.validator(value);
7966             if(msg !== true){
7967                 return false;
7968             }
7969         }
7970         
7971         if(this.regex && !this.regex.test(value)){
7972             return false;
7973         }
7974         
7975         return true;
7976     },
7977
7978     
7979     
7980      // private
7981     fireKey : function(e){
7982         //Roo.log('field ' + e.getKey());
7983         if(e.isNavKeyPress()){
7984             this.fireEvent("specialkey", this, e);
7985         }
7986     },
7987     focus : function (selectText){
7988         if(this.rendered){
7989             this.inputEl().focus();
7990             if(selectText === true){
7991                 this.inputEl().dom.select();
7992             }
7993         }
7994         return this;
7995     } ,
7996     
7997     onFocus : function(){
7998         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7999            // this.el.addClass(this.focusClass);
8000         }
8001         if(!this.hasFocus){
8002             this.hasFocus = true;
8003             this.startValue = this.getValue();
8004             this.fireEvent("focus", this);
8005         }
8006     },
8007     
8008     beforeBlur : Roo.emptyFn,
8009
8010     
8011     // private
8012     onBlur : function(){
8013         this.beforeBlur();
8014         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8015             //this.el.removeClass(this.focusClass);
8016         }
8017         this.hasFocus = false;
8018         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8019             this.validate();
8020         }
8021         var v = this.getValue();
8022         if(String(v) !== String(this.startValue)){
8023             this.fireEvent('change', this, v, this.startValue);
8024         }
8025         this.fireEvent("blur", this);
8026     },
8027     
8028     /**
8029      * Resets the current field value to the originally loaded value and clears any validation messages
8030      */
8031     reset : function(){
8032         this.setValue(this.originalValue);
8033         this.validate();
8034     },
8035      /**
8036      * Returns the name of the field
8037      * @return {Mixed} name The name field
8038      */
8039     getName: function(){
8040         return this.name;
8041     },
8042      /**
8043      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8044      * @return {Mixed} value The field value
8045      */
8046     getValue : function(){
8047         
8048         var v = this.inputEl().getValue();
8049         
8050         return v;
8051     },
8052     /**
8053      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8054      * @return {Mixed} value The field value
8055      */
8056     getRawValue : function(){
8057         var v = this.inputEl().getValue();
8058         
8059         return v;
8060     },
8061     
8062     /**
8063      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8064      * @param {Mixed} value The value to set
8065      */
8066     setRawValue : function(v){
8067         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8068     },
8069     
8070     selectText : function(start, end){
8071         var v = this.getRawValue();
8072         if(v.length > 0){
8073             start = start === undefined ? 0 : start;
8074             end = end === undefined ? v.length : end;
8075             var d = this.inputEl().dom;
8076             if(d.setSelectionRange){
8077                 d.setSelectionRange(start, end);
8078             }else if(d.createTextRange){
8079                 var range = d.createTextRange();
8080                 range.moveStart("character", start);
8081                 range.moveEnd("character", v.length-end);
8082                 range.select();
8083             }
8084         }
8085     },
8086     
8087     /**
8088      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8089      * @param {Mixed} value The value to set
8090      */
8091     setValue : function(v){
8092         this.value = v;
8093         if(this.rendered){
8094             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8095             this.validate();
8096         }
8097     },
8098     
8099     /*
8100     processValue : function(value){
8101         if(this.stripCharsRe){
8102             var newValue = value.replace(this.stripCharsRe, '');
8103             if(newValue !== value){
8104                 this.setRawValue(newValue);
8105                 return newValue;
8106             }
8107         }
8108         return value;
8109     },
8110   */
8111     preFocus : function(){
8112         
8113         if(this.selectOnFocus){
8114             this.inputEl().dom.select();
8115         }
8116     },
8117     filterKeys : function(e){
8118         var k = e.getKey();
8119         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8120             return;
8121         }
8122         var c = e.getCharCode(), cc = String.fromCharCode(c);
8123         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8124             return;
8125         }
8126         if(!this.maskRe.test(cc)){
8127             e.stopEvent();
8128         }
8129     },
8130      /**
8131      * Clear any invalid styles/messages for this field
8132      */
8133     clearInvalid : function(){
8134         
8135         if(!this.el || this.preventMark){ // not rendered
8136             return;
8137         }
8138         this.el.removeClass(this.invalidClass);
8139         
8140         this.fireEvent('valid', this);
8141     },
8142     
8143      /**
8144      * Mark this field as valid
8145      */
8146     markValid : function(){
8147         if(!this.el  || this.preventMark){ // not rendered
8148             return;
8149         }
8150         
8151         this.el.removeClass([this.invalidClass, this.validClass]);
8152         
8153         if(this.disabled || this.allowBlank){
8154             return;
8155         }
8156         
8157         this.el.addClass(this.validClass);
8158         
8159         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
8160             
8161             var feedback = this.el.select('.form-control-feedback', true).first();
8162             
8163             if(feedback){
8164                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8165                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8166             }
8167             
8168         }
8169         
8170         this.fireEvent('valid', this);
8171     },
8172     
8173      /**
8174      * Mark this field as invalid
8175      * @param {String} msg The validation message
8176      */
8177     markInvalid : function(msg){
8178         if(!this.el  || this.preventMark){ // not rendered
8179             return;
8180         }
8181         
8182         this.el.removeClass([this.invalidClass, this.validClass]);
8183         
8184         if(this.disabled || this.allowBlank){
8185             return;
8186         }
8187         
8188         this.el.addClass(this.invalidClass);
8189         
8190         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8191             
8192             var feedback = this.el.select('.form-control-feedback', true).first();
8193             
8194             if(feedback){
8195                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8196                 
8197                 if(this.getValue().length){
8198                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8199                 }
8200                 
8201             }
8202             
8203         }
8204         
8205         this.fireEvent('invalid', this, msg);
8206     },
8207     // private
8208     SafariOnKeyDown : function(event)
8209     {
8210         // this is a workaround for a password hang bug on chrome/ webkit.
8211         
8212         var isSelectAll = false;
8213         
8214         if(this.inputEl().dom.selectionEnd > 0){
8215             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8216         }
8217         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8218             event.preventDefault();
8219             this.setValue('');
8220             return;
8221         }
8222         
8223         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8224             
8225             event.preventDefault();
8226             // this is very hacky as keydown always get's upper case.
8227             //
8228             var cc = String.fromCharCode(event.getCharCode());
8229             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8230             
8231         }
8232     },
8233     adjustWidth : function(tag, w){
8234         tag = tag.toLowerCase();
8235         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8236             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8237                 if(tag == 'input'){
8238                     return w + 2;
8239                 }
8240                 if(tag == 'textarea'){
8241                     return w-2;
8242                 }
8243             }else if(Roo.isOpera){
8244                 if(tag == 'input'){
8245                     return w + 2;
8246                 }
8247                 if(tag == 'textarea'){
8248                     return w-2;
8249                 }
8250             }
8251         }
8252         return w;
8253     }
8254     
8255 });
8256
8257  
8258 /*
8259  * - LGPL
8260  *
8261  * Input
8262  * 
8263  */
8264
8265 /**
8266  * @class Roo.bootstrap.TextArea
8267  * @extends Roo.bootstrap.Input
8268  * Bootstrap TextArea class
8269  * @cfg {Number} cols Specifies the visible width of a text area
8270  * @cfg {Number} rows Specifies the visible number of lines in a text area
8271  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8272  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8273  * @cfg {string} html text
8274  * 
8275  * @constructor
8276  * Create a new TextArea
8277  * @param {Object} config The config object
8278  */
8279
8280 Roo.bootstrap.TextArea = function(config){
8281     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8282    
8283 };
8284
8285 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8286      
8287     cols : false,
8288     rows : 5,
8289     readOnly : false,
8290     warp : 'soft',
8291     resize : false,
8292     value: false,
8293     html: false,
8294     
8295     getAutoCreate : function(){
8296         
8297         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8298         
8299         var id = Roo.id();
8300         
8301         var cfg = {};
8302         
8303         var input =  {
8304             tag: 'textarea',
8305             id : id,
8306             warp : this.warp,
8307             rows : this.rows,
8308             value : this.value || '',
8309             html: this.html || '',
8310             cls : 'form-control',
8311             placeholder : this.placeholder || '' 
8312             
8313         };
8314         
8315         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8316             input.maxLength = this.maxLength;
8317         }
8318         
8319         if(this.resize){
8320             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8321         }
8322         
8323         if(this.cols){
8324             input.cols = this.cols;
8325         }
8326         
8327         if (this.readOnly) {
8328             input.readonly = true;
8329         }
8330         
8331         if (this.name) {
8332             input.name = this.name;
8333         }
8334         
8335         if (this.size) {
8336             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8337         }
8338         
8339         var settings=this;
8340         ['xs','sm','md','lg'].map(function(size){
8341             if (settings[size]) {
8342                 cfg.cls += ' col-' + size + '-' + settings[size];
8343             }
8344         });
8345         
8346         var inputblock = input;
8347         
8348         if(this.hasFeedback && !this.allowBlank){
8349             
8350             var feedback = {
8351                 tag: 'span',
8352                 cls: 'glyphicon form-control-feedback'
8353             };
8354
8355             inputblock = {
8356                 cls : 'has-feedback',
8357                 cn :  [
8358                     input,
8359                     feedback
8360                 ] 
8361             };  
8362         }
8363         
8364         
8365         if (this.before || this.after) {
8366             
8367             inputblock = {
8368                 cls : 'input-group',
8369                 cn :  [] 
8370             };
8371             if (this.before) {
8372                 inputblock.cn.push({
8373                     tag :'span',
8374                     cls : 'input-group-addon',
8375                     html : this.before
8376                 });
8377             }
8378             
8379             inputblock.cn.push(input);
8380             
8381             if(this.hasFeedback && !this.allowBlank){
8382                 inputblock.cls += ' has-feedback';
8383                 inputblock.cn.push(feedback);
8384             }
8385             
8386             if (this.after) {
8387                 inputblock.cn.push({
8388                     tag :'span',
8389                     cls : 'input-group-addon',
8390                     html : this.after
8391                 });
8392             }
8393             
8394         }
8395         
8396         if (align ==='left' && this.fieldLabel.length) {
8397                 Roo.log("left and has label");
8398                 cfg.cn = [
8399                     
8400                     {
8401                         tag: 'label',
8402                         'for' :  id,
8403                         cls : 'control-label col-sm-' + this.labelWidth,
8404                         html : this.fieldLabel
8405                         
8406                     },
8407                     {
8408                         cls : "col-sm-" + (12 - this.labelWidth), 
8409                         cn: [
8410                             inputblock
8411                         ]
8412                     }
8413                     
8414                 ];
8415         } else if ( this.fieldLabel.length) {
8416                 Roo.log(" label");
8417                  cfg.cn = [
8418                    
8419                     {
8420                         tag: 'label',
8421                         //cls : 'input-group-addon',
8422                         html : this.fieldLabel
8423                         
8424                     },
8425                     
8426                     inputblock
8427                     
8428                 ];
8429
8430         } else {
8431             
8432                    Roo.log(" no label && no align");
8433                 cfg.cn = [
8434                     
8435                         inputblock
8436                     
8437                 ];
8438                 
8439                 
8440         }
8441         
8442         if (this.disabled) {
8443             input.disabled=true;
8444         }
8445         
8446         return cfg;
8447         
8448     },
8449     /**
8450      * return the real textarea element.
8451      */
8452     inputEl: function ()
8453     {
8454         return this.el.select('textarea.form-control',true).first();
8455     }
8456 });
8457
8458  
8459 /*
8460  * - LGPL
8461  *
8462  * trigger field - base class for combo..
8463  * 
8464  */
8465  
8466 /**
8467  * @class Roo.bootstrap.TriggerField
8468  * @extends Roo.bootstrap.Input
8469  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8470  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8471  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8472  * for which you can provide a custom implementation.  For example:
8473  * <pre><code>
8474 var trigger = new Roo.bootstrap.TriggerField();
8475 trigger.onTriggerClick = myTriggerFn;
8476 trigger.applyTo('my-field');
8477 </code></pre>
8478  *
8479  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8480  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8481  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8482  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8483  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8484
8485  * @constructor
8486  * Create a new TriggerField.
8487  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8488  * to the base TextField)
8489  */
8490 Roo.bootstrap.TriggerField = function(config){
8491     this.mimicing = false;
8492     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8493 };
8494
8495 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8496     /**
8497      * @cfg {String} triggerClass A CSS class to apply to the trigger
8498      */
8499      /**
8500      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8501      */
8502     hideTrigger:false,
8503
8504     /**
8505      * @cfg {Boolean} removable (true|false) special filter default false
8506      */
8507     removable : false,
8508     
8509     /** @cfg {Boolean} grow @hide */
8510     /** @cfg {Number} growMin @hide */
8511     /** @cfg {Number} growMax @hide */
8512
8513     /**
8514      * @hide 
8515      * @method
8516      */
8517     autoSize: Roo.emptyFn,
8518     // private
8519     monitorTab : true,
8520     // private
8521     deferHeight : true,
8522
8523     
8524     actionMode : 'wrap',
8525     
8526     caret : false,
8527     
8528     
8529     getAutoCreate : function(){
8530        
8531         var align = this.labelAlign || this.parentLabelAlign();
8532         
8533         var id = Roo.id();
8534         
8535         var cfg = {
8536             cls: 'form-group' //input-group
8537         };
8538         
8539         
8540         var input =  {
8541             tag: 'input',
8542             id : id,
8543             type : this.inputType,
8544             cls : 'form-control',
8545             autocomplete: 'new-password',
8546             placeholder : this.placeholder || '' 
8547             
8548         };
8549         if (this.name) {
8550             input.name = this.name;
8551         }
8552         if (this.size) {
8553             input.cls += ' input-' + this.size;
8554         }
8555         
8556         if (this.disabled) {
8557             input.disabled=true;
8558         }
8559         
8560         var inputblock = input;
8561         
8562         if(this.hasFeedback && !this.allowBlank){
8563             
8564             var feedback = {
8565                 tag: 'span',
8566                 cls: 'glyphicon form-control-feedback'
8567             };
8568             
8569             if(this.removable && !this.editable && !this.tickable){
8570                 inputblock = {
8571                     cls : 'has-feedback',
8572                     cn :  [
8573                         inputblock,
8574                         {
8575                             tag: 'button',
8576                             html : 'x',
8577                             cls : 'roo-combo-removable-btn close'
8578                         },
8579                         feedback
8580                     ] 
8581                 };
8582             } else {
8583                 inputblock = {
8584                     cls : 'has-feedback',
8585                     cn :  [
8586                         inputblock,
8587                         feedback
8588                     ] 
8589                 };
8590             }
8591
8592         } else {
8593             if(this.removable && !this.editable && !this.tickable){
8594                 inputblock = {
8595                     cls : 'roo-removable',
8596                     cn :  [
8597                         inputblock,
8598                         {
8599                             tag: 'button',
8600                             html : 'x',
8601                             cls : 'roo-combo-removable-btn close'
8602                         }
8603                     ] 
8604                 };
8605             }
8606         }
8607         
8608         if (this.before || this.after) {
8609             
8610             inputblock = {
8611                 cls : 'input-group',
8612                 cn :  [] 
8613             };
8614             if (this.before) {
8615                 inputblock.cn.push({
8616                     tag :'span',
8617                     cls : 'input-group-addon',
8618                     html : this.before
8619                 });
8620             }
8621             
8622             inputblock.cn.push(input);
8623             
8624             if(this.hasFeedback && !this.allowBlank){
8625                 inputblock.cls += ' has-feedback';
8626                 inputblock.cn.push(feedback);
8627             }
8628             
8629             if (this.after) {
8630                 inputblock.cn.push({
8631                     tag :'span',
8632                     cls : 'input-group-addon',
8633                     html : this.after
8634                 });
8635             }
8636             
8637         };
8638         
8639         var box = {
8640             tag: 'div',
8641             cn: [
8642                 {
8643                     tag: 'input',
8644                     type : 'hidden',
8645                     cls: 'form-hidden-field'
8646                 },
8647                 inputblock
8648             ]
8649             
8650         };
8651         
8652         if(this.multiple){
8653             Roo.log('multiple');
8654             
8655             box = {
8656                 tag: 'div',
8657                 cn: [
8658                     {
8659                         tag: 'input',
8660                         type : 'hidden',
8661                         cls: 'form-hidden-field'
8662                     },
8663                     {
8664                         tag: 'ul',
8665                         cls: 'select2-choices',
8666                         cn:[
8667                             {
8668                                 tag: 'li',
8669                                 cls: 'select2-search-field',
8670                                 cn: [
8671
8672                                     inputblock
8673                                 ]
8674                             }
8675                         ]
8676                     }
8677                 ]
8678             }
8679         };
8680         
8681         var combobox = {
8682             cls: 'select2-container input-group',
8683             cn: [
8684                 box
8685 //                {
8686 //                    tag: 'ul',
8687 //                    cls: 'typeahead typeahead-long dropdown-menu',
8688 //                    style: 'display:none'
8689 //                }
8690             ]
8691         };
8692         
8693         if(!this.multiple && this.showToggleBtn){
8694             
8695             var caret = {
8696                         tag: 'span',
8697                         cls: 'caret'
8698              };
8699             if (this.caret != false) {
8700                 caret = {
8701                      tag: 'i',
8702                      cls: 'fa fa-' + this.caret
8703                 };
8704                 
8705             }
8706             
8707             combobox.cn.push({
8708                 tag :'span',
8709                 cls : 'input-group-addon btn dropdown-toggle',
8710                 cn : [
8711                     caret,
8712                     {
8713                         tag: 'span',
8714                         cls: 'combobox-clear',
8715                         cn  : [
8716                             {
8717                                 tag : 'i',
8718                                 cls: 'icon-remove'
8719                             }
8720                         ]
8721                     }
8722                 ]
8723
8724             })
8725         }
8726         
8727         if(this.multiple){
8728             combobox.cls += ' select2-container-multi';
8729         }
8730         
8731         if (align ==='left' && this.fieldLabel.length) {
8732             
8733                 Roo.log("left and has label");
8734                 cfg.cn = [
8735                     
8736                     {
8737                         tag: 'label',
8738                         'for' :  id,
8739                         cls : 'control-label col-sm-' + this.labelWidth,
8740                         html : this.fieldLabel
8741                         
8742                     },
8743                     {
8744                         cls : "col-sm-" + (12 - this.labelWidth), 
8745                         cn: [
8746                             combobox
8747                         ]
8748                     }
8749                     
8750                 ];
8751         } else if ( this.fieldLabel.length) {
8752                 Roo.log(" label");
8753                  cfg.cn = [
8754                    
8755                     {
8756                         tag: 'label',
8757                         //cls : 'input-group-addon',
8758                         html : this.fieldLabel
8759                         
8760                     },
8761                     
8762                     combobox
8763                     
8764                 ];
8765
8766         } else {
8767             
8768                 Roo.log(" no label && no align");
8769                 cfg = combobox
8770                      
8771                 
8772         }
8773          
8774         var settings=this;
8775         ['xs','sm','md','lg'].map(function(size){
8776             if (settings[size]) {
8777                 cfg.cls += ' col-' + size + '-' + settings[size];
8778             }
8779         });
8780         Roo.log(cfg);
8781         return cfg;
8782         
8783     },
8784     
8785     
8786     
8787     // private
8788     onResize : function(w, h){
8789 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8790 //        if(typeof w == 'number'){
8791 //            var x = w - this.trigger.getWidth();
8792 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8793 //            this.trigger.setStyle('left', x+'px');
8794 //        }
8795     },
8796
8797     // private
8798     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8799
8800     // private
8801     getResizeEl : function(){
8802         return this.inputEl();
8803     },
8804
8805     // private
8806     getPositionEl : function(){
8807         return this.inputEl();
8808     },
8809
8810     // private
8811     alignErrorIcon : function(){
8812         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8813     },
8814
8815     // private
8816     initEvents : function(){
8817         
8818         this.createList();
8819         
8820         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8821         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8822         if(!this.multiple && this.showToggleBtn){
8823             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8824             if(this.hideTrigger){
8825                 this.trigger.setDisplayed(false);
8826             }
8827             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8828         }
8829         
8830         if(this.multiple){
8831             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8832         }
8833         
8834         if(this.removable && !this.editable && !this.tickable){
8835             var close = this.closeTriggerEl();
8836             
8837             if(close){
8838                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8839                 close.on('click', this.removeBtnClick, this, close);
8840             }
8841         }
8842         
8843         //this.trigger.addClassOnOver('x-form-trigger-over');
8844         //this.trigger.addClassOnClick('x-form-trigger-click');
8845         
8846         //if(!this.width){
8847         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8848         //}
8849     },
8850     
8851     closeTriggerEl : function()
8852     {
8853         var close = this.el.select('.roo-combo-removable-btn', true).first();
8854         return close ? close : false;
8855     },
8856     
8857     removeBtnClick : function(e, h, el)
8858     {
8859         e.preventDefault();
8860         
8861         if(this.fireEvent("remove", this) !== false){
8862             this.reset();
8863         }
8864     },
8865     
8866     createList : function()
8867     {
8868         this.list = Roo.get(document.body).createChild({
8869             tag: 'ul',
8870             cls: 'typeahead typeahead-long dropdown-menu',
8871             style: 'display:none'
8872         });
8873         
8874         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8875         
8876     },
8877
8878     // private
8879     initTrigger : function(){
8880        
8881     },
8882
8883     // private
8884     onDestroy : function(){
8885         if(this.trigger){
8886             this.trigger.removeAllListeners();
8887           //  this.trigger.remove();
8888         }
8889         //if(this.wrap){
8890         //    this.wrap.remove();
8891         //}
8892         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8893     },
8894
8895     // private
8896     onFocus : function(){
8897         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8898         /*
8899         if(!this.mimicing){
8900             this.wrap.addClass('x-trigger-wrap-focus');
8901             this.mimicing = true;
8902             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8903             if(this.monitorTab){
8904                 this.el.on("keydown", this.checkTab, this);
8905             }
8906         }
8907         */
8908     },
8909
8910     // private
8911     checkTab : function(e){
8912         if(e.getKey() == e.TAB){
8913             this.triggerBlur();
8914         }
8915     },
8916
8917     // private
8918     onBlur : function(){
8919         // do nothing
8920     },
8921
8922     // private
8923     mimicBlur : function(e, t){
8924         /*
8925         if(!this.wrap.contains(t) && this.validateBlur()){
8926             this.triggerBlur();
8927         }
8928         */
8929     },
8930
8931     // private
8932     triggerBlur : function(){
8933         this.mimicing = false;
8934         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8935         if(this.monitorTab){
8936             this.el.un("keydown", this.checkTab, this);
8937         }
8938         //this.wrap.removeClass('x-trigger-wrap-focus');
8939         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8940     },
8941
8942     // private
8943     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8944     validateBlur : function(e, t){
8945         return true;
8946     },
8947
8948     // private
8949     onDisable : function(){
8950         this.inputEl().dom.disabled = true;
8951         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8952         //if(this.wrap){
8953         //    this.wrap.addClass('x-item-disabled');
8954         //}
8955     },
8956
8957     // private
8958     onEnable : function(){
8959         this.inputEl().dom.disabled = false;
8960         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8961         //if(this.wrap){
8962         //    this.el.removeClass('x-item-disabled');
8963         //}
8964     },
8965
8966     // private
8967     onShow : function(){
8968         var ae = this.getActionEl();
8969         
8970         if(ae){
8971             ae.dom.style.display = '';
8972             ae.dom.style.visibility = 'visible';
8973         }
8974     },
8975
8976     // private
8977     
8978     onHide : function(){
8979         var ae = this.getActionEl();
8980         ae.dom.style.display = 'none';
8981     },
8982
8983     /**
8984      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8985      * by an implementing function.
8986      * @method
8987      * @param {EventObject} e
8988      */
8989     onTriggerClick : Roo.emptyFn
8990 });
8991  /*
8992  * Based on:
8993  * Ext JS Library 1.1.1
8994  * Copyright(c) 2006-2007, Ext JS, LLC.
8995  *
8996  * Originally Released Under LGPL - original licence link has changed is not relivant.
8997  *
8998  * Fork - LGPL
8999  * <script type="text/javascript">
9000  */
9001
9002
9003 /**
9004  * @class Roo.data.SortTypes
9005  * @singleton
9006  * Defines the default sorting (casting?) comparison functions used when sorting data.
9007  */
9008 Roo.data.SortTypes = {
9009     /**
9010      * Default sort that does nothing
9011      * @param {Mixed} s The value being converted
9012      * @return {Mixed} The comparison value
9013      */
9014     none : function(s){
9015         return s;
9016     },
9017     
9018     /**
9019      * The regular expression used to strip tags
9020      * @type {RegExp}
9021      * @property
9022      */
9023     stripTagsRE : /<\/?[^>]+>/gi,
9024     
9025     /**
9026      * Strips all HTML tags to sort on text only
9027      * @param {Mixed} s The value being converted
9028      * @return {String} The comparison value
9029      */
9030     asText : function(s){
9031         return String(s).replace(this.stripTagsRE, "");
9032     },
9033     
9034     /**
9035      * Strips all HTML tags to sort on text only - Case insensitive
9036      * @param {Mixed} s The value being converted
9037      * @return {String} The comparison value
9038      */
9039     asUCText : function(s){
9040         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9041     },
9042     
9043     /**
9044      * Case insensitive string
9045      * @param {Mixed} s The value being converted
9046      * @return {String} The comparison value
9047      */
9048     asUCString : function(s) {
9049         return String(s).toUpperCase();
9050     },
9051     
9052     /**
9053      * Date sorting
9054      * @param {Mixed} s The value being converted
9055      * @return {Number} The comparison value
9056      */
9057     asDate : function(s) {
9058         if(!s){
9059             return 0;
9060         }
9061         if(s instanceof Date){
9062             return s.getTime();
9063         }
9064         return Date.parse(String(s));
9065     },
9066     
9067     /**
9068      * Float sorting
9069      * @param {Mixed} s The value being converted
9070      * @return {Float} The comparison value
9071      */
9072     asFloat : function(s) {
9073         var val = parseFloat(String(s).replace(/,/g, ""));
9074         if(isNaN(val)) val = 0;
9075         return val;
9076     },
9077     
9078     /**
9079      * Integer sorting
9080      * @param {Mixed} s The value being converted
9081      * @return {Number} The comparison value
9082      */
9083     asInt : function(s) {
9084         var val = parseInt(String(s).replace(/,/g, ""));
9085         if(isNaN(val)) val = 0;
9086         return val;
9087     }
9088 };/*
9089  * Based on:
9090  * Ext JS Library 1.1.1
9091  * Copyright(c) 2006-2007, Ext JS, LLC.
9092  *
9093  * Originally Released Under LGPL - original licence link has changed is not relivant.
9094  *
9095  * Fork - LGPL
9096  * <script type="text/javascript">
9097  */
9098
9099 /**
9100 * @class Roo.data.Record
9101  * Instances of this class encapsulate both record <em>definition</em> information, and record
9102  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9103  * to access Records cached in an {@link Roo.data.Store} object.<br>
9104  * <p>
9105  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9106  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9107  * objects.<br>
9108  * <p>
9109  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9110  * @constructor
9111  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9112  * {@link #create}. The parameters are the same.
9113  * @param {Array} data An associative Array of data values keyed by the field name.
9114  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9115  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9116  * not specified an integer id is generated.
9117  */
9118 Roo.data.Record = function(data, id){
9119     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9120     this.data = data;
9121 };
9122
9123 /**
9124  * Generate a constructor for a specific record layout.
9125  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9126  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9127  * Each field definition object may contain the following properties: <ul>
9128  * <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,
9129  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9130  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9131  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9132  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9133  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9134  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9135  * this may be omitted.</p></li>
9136  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9137  * <ul><li>auto (Default, implies no conversion)</li>
9138  * <li>string</li>
9139  * <li>int</li>
9140  * <li>float</li>
9141  * <li>boolean</li>
9142  * <li>date</li></ul></p></li>
9143  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9144  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9145  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9146  * by the Reader into an object that will be stored in the Record. It is passed the
9147  * following parameters:<ul>
9148  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9149  * </ul></p></li>
9150  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9151  * </ul>
9152  * <br>usage:<br><pre><code>
9153 var TopicRecord = Roo.data.Record.create(
9154     {name: 'title', mapping: 'topic_title'},
9155     {name: 'author', mapping: 'username'},
9156     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9157     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9158     {name: 'lastPoster', mapping: 'user2'},
9159     {name: 'excerpt', mapping: 'post_text'}
9160 );
9161
9162 var myNewRecord = new TopicRecord({
9163     title: 'Do my job please',
9164     author: 'noobie',
9165     totalPosts: 1,
9166     lastPost: new Date(),
9167     lastPoster: 'Animal',
9168     excerpt: 'No way dude!'
9169 });
9170 myStore.add(myNewRecord);
9171 </code></pre>
9172  * @method create
9173  * @static
9174  */
9175 Roo.data.Record.create = function(o){
9176     var f = function(){
9177         f.superclass.constructor.apply(this, arguments);
9178     };
9179     Roo.extend(f, Roo.data.Record);
9180     var p = f.prototype;
9181     p.fields = new Roo.util.MixedCollection(false, function(field){
9182         return field.name;
9183     });
9184     for(var i = 0, len = o.length; i < len; i++){
9185         p.fields.add(new Roo.data.Field(o[i]));
9186     }
9187     f.getField = function(name){
9188         return p.fields.get(name);  
9189     };
9190     return f;
9191 };
9192
9193 Roo.data.Record.AUTO_ID = 1000;
9194 Roo.data.Record.EDIT = 'edit';
9195 Roo.data.Record.REJECT = 'reject';
9196 Roo.data.Record.COMMIT = 'commit';
9197
9198 Roo.data.Record.prototype = {
9199     /**
9200      * Readonly flag - true if this record has been modified.
9201      * @type Boolean
9202      */
9203     dirty : false,
9204     editing : false,
9205     error: null,
9206     modified: null,
9207
9208     // private
9209     join : function(store){
9210         this.store = store;
9211     },
9212
9213     /**
9214      * Set the named field to the specified value.
9215      * @param {String} name The name of the field to set.
9216      * @param {Object} value The value to set the field to.
9217      */
9218     set : function(name, value){
9219         if(this.data[name] == value){
9220             return;
9221         }
9222         this.dirty = true;
9223         if(!this.modified){
9224             this.modified = {};
9225         }
9226         if(typeof this.modified[name] == 'undefined'){
9227             this.modified[name] = this.data[name];
9228         }
9229         this.data[name] = value;
9230         if(!this.editing && this.store){
9231             this.store.afterEdit(this);
9232         }       
9233     },
9234
9235     /**
9236      * Get the value of the named field.
9237      * @param {String} name The name of the field to get the value of.
9238      * @return {Object} The value of the field.
9239      */
9240     get : function(name){
9241         return this.data[name]; 
9242     },
9243
9244     // private
9245     beginEdit : function(){
9246         this.editing = true;
9247         this.modified = {}; 
9248     },
9249
9250     // private
9251     cancelEdit : function(){
9252         this.editing = false;
9253         delete this.modified;
9254     },
9255
9256     // private
9257     endEdit : function(){
9258         this.editing = false;
9259         if(this.dirty && this.store){
9260             this.store.afterEdit(this);
9261         }
9262     },
9263
9264     /**
9265      * Usually called by the {@link Roo.data.Store} which owns the Record.
9266      * Rejects all changes made to the Record since either creation, or the last commit operation.
9267      * Modified fields are reverted to their original values.
9268      * <p>
9269      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9270      * of reject operations.
9271      */
9272     reject : function(){
9273         var m = this.modified;
9274         for(var n in m){
9275             if(typeof m[n] != "function"){
9276                 this.data[n] = m[n];
9277             }
9278         }
9279         this.dirty = false;
9280         delete this.modified;
9281         this.editing = false;
9282         if(this.store){
9283             this.store.afterReject(this);
9284         }
9285     },
9286
9287     /**
9288      * Usually called by the {@link Roo.data.Store} which owns the Record.
9289      * Commits all changes made to the Record since either creation, or the last commit operation.
9290      * <p>
9291      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9292      * of commit operations.
9293      */
9294     commit : function(){
9295         this.dirty = false;
9296         delete this.modified;
9297         this.editing = false;
9298         if(this.store){
9299             this.store.afterCommit(this);
9300         }
9301     },
9302
9303     // private
9304     hasError : function(){
9305         return this.error != null;
9306     },
9307
9308     // private
9309     clearError : function(){
9310         this.error = null;
9311     },
9312
9313     /**
9314      * Creates a copy of this record.
9315      * @param {String} id (optional) A new record id if you don't want to use this record's id
9316      * @return {Record}
9317      */
9318     copy : function(newId) {
9319         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9320     }
9321 };/*
9322  * Based on:
9323  * Ext JS Library 1.1.1
9324  * Copyright(c) 2006-2007, Ext JS, LLC.
9325  *
9326  * Originally Released Under LGPL - original licence link has changed is not relivant.
9327  *
9328  * Fork - LGPL
9329  * <script type="text/javascript">
9330  */
9331
9332
9333
9334 /**
9335  * @class Roo.data.Store
9336  * @extends Roo.util.Observable
9337  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9338  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9339  * <p>
9340  * 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
9341  * has no knowledge of the format of the data returned by the Proxy.<br>
9342  * <p>
9343  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9344  * instances from the data object. These records are cached and made available through accessor functions.
9345  * @constructor
9346  * Creates a new Store.
9347  * @param {Object} config A config object containing the objects needed for the Store to access data,
9348  * and read the data into Records.
9349  */
9350 Roo.data.Store = function(config){
9351     this.data = new Roo.util.MixedCollection(false);
9352     this.data.getKey = function(o){
9353         return o.id;
9354     };
9355     this.baseParams = {};
9356     // private
9357     this.paramNames = {
9358         "start" : "start",
9359         "limit" : "limit",
9360         "sort" : "sort",
9361         "dir" : "dir",
9362         "multisort" : "_multisort"
9363     };
9364
9365     if(config && config.data){
9366         this.inlineData = config.data;
9367         delete config.data;
9368     }
9369
9370     Roo.apply(this, config);
9371     
9372     if(this.reader){ // reader passed
9373         this.reader = Roo.factory(this.reader, Roo.data);
9374         this.reader.xmodule = this.xmodule || false;
9375         if(!this.recordType){
9376             this.recordType = this.reader.recordType;
9377         }
9378         if(this.reader.onMetaChange){
9379             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9380         }
9381     }
9382
9383     if(this.recordType){
9384         this.fields = this.recordType.prototype.fields;
9385     }
9386     this.modified = [];
9387
9388     this.addEvents({
9389         /**
9390          * @event datachanged
9391          * Fires when the data cache has changed, and a widget which is using this Store
9392          * as a Record cache should refresh its view.
9393          * @param {Store} this
9394          */
9395         datachanged : true,
9396         /**
9397          * @event metachange
9398          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9399          * @param {Store} this
9400          * @param {Object} meta The JSON metadata
9401          */
9402         metachange : true,
9403         /**
9404          * @event add
9405          * Fires when Records have been added to the Store
9406          * @param {Store} this
9407          * @param {Roo.data.Record[]} records The array of Records added
9408          * @param {Number} index The index at which the record(s) were added
9409          */
9410         add : true,
9411         /**
9412          * @event remove
9413          * Fires when a Record has been removed from the Store
9414          * @param {Store} this
9415          * @param {Roo.data.Record} record The Record that was removed
9416          * @param {Number} index The index at which the record was removed
9417          */
9418         remove : true,
9419         /**
9420          * @event update
9421          * Fires when a Record has been updated
9422          * @param {Store} this
9423          * @param {Roo.data.Record} record The Record that was updated
9424          * @param {String} operation The update operation being performed.  Value may be one of:
9425          * <pre><code>
9426  Roo.data.Record.EDIT
9427  Roo.data.Record.REJECT
9428  Roo.data.Record.COMMIT
9429          * </code></pre>
9430          */
9431         update : true,
9432         /**
9433          * @event clear
9434          * Fires when the data cache has been cleared.
9435          * @param {Store} this
9436          */
9437         clear : true,
9438         /**
9439          * @event beforeload
9440          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9441          * the load action will be canceled.
9442          * @param {Store} this
9443          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9444          */
9445         beforeload : true,
9446         /**
9447          * @event beforeloadadd
9448          * Fires after a new set of Records has been loaded.
9449          * @param {Store} this
9450          * @param {Roo.data.Record[]} records The Records that were loaded
9451          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9452          */
9453         beforeloadadd : true,
9454         /**
9455          * @event load
9456          * Fires after a new set of Records has been loaded, before they are added to the store.
9457          * @param {Store} this
9458          * @param {Roo.data.Record[]} records The Records that were loaded
9459          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9460          * @params {Object} return from reader
9461          */
9462         load : true,
9463         /**
9464          * @event loadexception
9465          * Fires if an exception occurs in the Proxy during loading.
9466          * Called with the signature of the Proxy's "loadexception" event.
9467          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9468          * 
9469          * @param {Proxy} 
9470          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9471          * @param {Object} load options 
9472          * @param {Object} jsonData from your request (normally this contains the Exception)
9473          */
9474         loadexception : true
9475     });
9476     
9477     if(this.proxy){
9478         this.proxy = Roo.factory(this.proxy, Roo.data);
9479         this.proxy.xmodule = this.xmodule || false;
9480         this.relayEvents(this.proxy,  ["loadexception"]);
9481     }
9482     this.sortToggle = {};
9483     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9484
9485     Roo.data.Store.superclass.constructor.call(this);
9486
9487     if(this.inlineData){
9488         this.loadData(this.inlineData);
9489         delete this.inlineData;
9490     }
9491 };
9492
9493 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9494      /**
9495     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9496     * without a remote query - used by combo/forms at present.
9497     */
9498     
9499     /**
9500     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9501     */
9502     /**
9503     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9504     */
9505     /**
9506     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9507     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9508     */
9509     /**
9510     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9511     * on any HTTP request
9512     */
9513     /**
9514     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9515     */
9516     /**
9517     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9518     */
9519     multiSort: false,
9520     /**
9521     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9522     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9523     */
9524     remoteSort : false,
9525
9526     /**
9527     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9528      * loaded or when a record is removed. (defaults to false).
9529     */
9530     pruneModifiedRecords : false,
9531
9532     // private
9533     lastOptions : null,
9534
9535     /**
9536      * Add Records to the Store and fires the add event.
9537      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9538      */
9539     add : function(records){
9540         records = [].concat(records);
9541         for(var i = 0, len = records.length; i < len; i++){
9542             records[i].join(this);
9543         }
9544         var index = this.data.length;
9545         this.data.addAll(records);
9546         this.fireEvent("add", this, records, index);
9547     },
9548
9549     /**
9550      * Remove a Record from the Store and fires the remove event.
9551      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9552      */
9553     remove : function(record){
9554         var index = this.data.indexOf(record);
9555         this.data.removeAt(index);
9556         if(this.pruneModifiedRecords){
9557             this.modified.remove(record);
9558         }
9559         this.fireEvent("remove", this, record, index);
9560     },
9561
9562     /**
9563      * Remove all Records from the Store and fires the clear event.
9564      */
9565     removeAll : function(){
9566         this.data.clear();
9567         if(this.pruneModifiedRecords){
9568             this.modified = [];
9569         }
9570         this.fireEvent("clear", this);
9571     },
9572
9573     /**
9574      * Inserts Records to the Store at the given index and fires the add event.
9575      * @param {Number} index The start index at which to insert the passed Records.
9576      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9577      */
9578     insert : function(index, records){
9579         records = [].concat(records);
9580         for(var i = 0, len = records.length; i < len; i++){
9581             this.data.insert(index, records[i]);
9582             records[i].join(this);
9583         }
9584         this.fireEvent("add", this, records, index);
9585     },
9586
9587     /**
9588      * Get the index within the cache of the passed Record.
9589      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9590      * @return {Number} The index of the passed Record. Returns -1 if not found.
9591      */
9592     indexOf : function(record){
9593         return this.data.indexOf(record);
9594     },
9595
9596     /**
9597      * Get the index within the cache of the Record with the passed id.
9598      * @param {String} id The id of the Record to find.
9599      * @return {Number} The index of the Record. Returns -1 if not found.
9600      */
9601     indexOfId : function(id){
9602         return this.data.indexOfKey(id);
9603     },
9604
9605     /**
9606      * Get the Record with the specified id.
9607      * @param {String} id The id of the Record to find.
9608      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9609      */
9610     getById : function(id){
9611         return this.data.key(id);
9612     },
9613
9614     /**
9615      * Get the Record at the specified index.
9616      * @param {Number} index The index of the Record to find.
9617      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9618      */
9619     getAt : function(index){
9620         return this.data.itemAt(index);
9621     },
9622
9623     /**
9624      * Returns a range of Records between specified indices.
9625      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9626      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9627      * @return {Roo.data.Record[]} An array of Records
9628      */
9629     getRange : function(start, end){
9630         return this.data.getRange(start, end);
9631     },
9632
9633     // private
9634     storeOptions : function(o){
9635         o = Roo.apply({}, o);
9636         delete o.callback;
9637         delete o.scope;
9638         this.lastOptions = o;
9639     },
9640
9641     /**
9642      * Loads the Record cache from the configured Proxy using the configured Reader.
9643      * <p>
9644      * If using remote paging, then the first load call must specify the <em>start</em>
9645      * and <em>limit</em> properties in the options.params property to establish the initial
9646      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9647      * <p>
9648      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9649      * and this call will return before the new data has been loaded. Perform any post-processing
9650      * in a callback function, or in a "load" event handler.</strong>
9651      * <p>
9652      * @param {Object} options An object containing properties which control loading options:<ul>
9653      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9654      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9655      * passed the following arguments:<ul>
9656      * <li>r : Roo.data.Record[]</li>
9657      * <li>options: Options object from the load call</li>
9658      * <li>success: Boolean success indicator</li></ul></li>
9659      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9660      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9661      * </ul>
9662      */
9663     load : function(options){
9664         options = options || {};
9665         if(this.fireEvent("beforeload", this, options) !== false){
9666             this.storeOptions(options);
9667             var p = Roo.apply(options.params || {}, this.baseParams);
9668             // if meta was not loaded from remote source.. try requesting it.
9669             if (!this.reader.metaFromRemote) {
9670                 p._requestMeta = 1;
9671             }
9672             if(this.sortInfo && this.remoteSort){
9673                 var pn = this.paramNames;
9674                 p[pn["sort"]] = this.sortInfo.field;
9675                 p[pn["dir"]] = this.sortInfo.direction;
9676             }
9677             if (this.multiSort) {
9678                 var pn = this.paramNames;
9679                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9680             }
9681             
9682             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9683         }
9684     },
9685
9686     /**
9687      * Reloads the Record cache from the configured Proxy using the configured Reader and
9688      * the options from the last load operation performed.
9689      * @param {Object} options (optional) An object containing properties which may override the options
9690      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9691      * the most recently used options are reused).
9692      */
9693     reload : function(options){
9694         this.load(Roo.applyIf(options||{}, this.lastOptions));
9695     },
9696
9697     // private
9698     // Called as a callback by the Reader during a load operation.
9699     loadRecords : function(o, options, success){
9700         if(!o || success === false){
9701             if(success !== false){
9702                 this.fireEvent("load", this, [], options, o);
9703             }
9704             if(options.callback){
9705                 options.callback.call(options.scope || this, [], options, false);
9706             }
9707             return;
9708         }
9709         // if data returned failure - throw an exception.
9710         if (o.success === false) {
9711             // show a message if no listener is registered.
9712             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9713                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9714             }
9715             // loadmask wil be hooked into this..
9716             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9717             return;
9718         }
9719         var r = o.records, t = o.totalRecords || r.length;
9720         
9721         this.fireEvent("beforeloadadd", this, r, options, o);
9722         
9723         if(!options || options.add !== true){
9724             if(this.pruneModifiedRecords){
9725                 this.modified = [];
9726             }
9727             for(var i = 0, len = r.length; i < len; i++){
9728                 r[i].join(this);
9729             }
9730             if(this.snapshot){
9731                 this.data = this.snapshot;
9732                 delete this.snapshot;
9733             }
9734             this.data.clear();
9735             this.data.addAll(r);
9736             this.totalLength = t;
9737             this.applySort();
9738             this.fireEvent("datachanged", this);
9739         }else{
9740             this.totalLength = Math.max(t, this.data.length+r.length);
9741             this.add(r);
9742         }
9743         this.fireEvent("load", this, r, options, o);
9744         if(options.callback){
9745             options.callback.call(options.scope || this, r, options, true);
9746         }
9747     },
9748
9749
9750     /**
9751      * Loads data from a passed data block. A Reader which understands the format of the data
9752      * must have been configured in the constructor.
9753      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9754      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9755      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9756      */
9757     loadData : function(o, append){
9758         var r = this.reader.readRecords(o);
9759         this.loadRecords(r, {add: append}, true);
9760     },
9761
9762     /**
9763      * Gets the number of cached records.
9764      * <p>
9765      * <em>If using paging, this may not be the total size of the dataset. If the data object
9766      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9767      * the data set size</em>
9768      */
9769     getCount : function(){
9770         return this.data.length || 0;
9771     },
9772
9773     /**
9774      * Gets the total number of records in the dataset as returned by the server.
9775      * <p>
9776      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9777      * the dataset size</em>
9778      */
9779     getTotalCount : function(){
9780         return this.totalLength || 0;
9781     },
9782
9783     /**
9784      * Returns the sort state of the Store as an object with two properties:
9785      * <pre><code>
9786  field {String} The name of the field by which the Records are sorted
9787  direction {String} The sort order, "ASC" or "DESC"
9788      * </code></pre>
9789      */
9790     getSortState : function(){
9791         return this.sortInfo;
9792     },
9793
9794     // private
9795     applySort : function(){
9796         if(this.sortInfo && !this.remoteSort){
9797             var s = this.sortInfo, f = s.field;
9798             var st = this.fields.get(f).sortType;
9799             var fn = function(r1, r2){
9800                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9801                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9802             };
9803             this.data.sort(s.direction, fn);
9804             if(this.snapshot && this.snapshot != this.data){
9805                 this.snapshot.sort(s.direction, fn);
9806             }
9807         }
9808     },
9809
9810     /**
9811      * Sets the default sort column and order to be used by the next load operation.
9812      * @param {String} fieldName The name of the field to sort by.
9813      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9814      */
9815     setDefaultSort : function(field, dir){
9816         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9817     },
9818
9819     /**
9820      * Sort the Records.
9821      * If remote sorting is used, the sort is performed on the server, and the cache is
9822      * reloaded. If local sorting is used, the cache is sorted internally.
9823      * @param {String} fieldName The name of the field to sort by.
9824      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9825      */
9826     sort : function(fieldName, dir){
9827         var f = this.fields.get(fieldName);
9828         if(!dir){
9829             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9830             
9831             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9832                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9833             }else{
9834                 dir = f.sortDir;
9835             }
9836         }
9837         this.sortToggle[f.name] = dir;
9838         this.sortInfo = {field: f.name, direction: dir};
9839         if(!this.remoteSort){
9840             this.applySort();
9841             this.fireEvent("datachanged", this);
9842         }else{
9843             this.load(this.lastOptions);
9844         }
9845     },
9846
9847     /**
9848      * Calls the specified function for each of the Records in the cache.
9849      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9850      * Returning <em>false</em> aborts and exits the iteration.
9851      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9852      */
9853     each : function(fn, scope){
9854         this.data.each(fn, scope);
9855     },
9856
9857     /**
9858      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9859      * (e.g., during paging).
9860      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9861      */
9862     getModifiedRecords : function(){
9863         return this.modified;
9864     },
9865
9866     // private
9867     createFilterFn : function(property, value, anyMatch){
9868         if(!value.exec){ // not a regex
9869             value = String(value);
9870             if(value.length == 0){
9871                 return false;
9872             }
9873             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9874         }
9875         return function(r){
9876             return value.test(r.data[property]);
9877         };
9878     },
9879
9880     /**
9881      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9882      * @param {String} property A field on your records
9883      * @param {Number} start The record index to start at (defaults to 0)
9884      * @param {Number} end The last record index to include (defaults to length - 1)
9885      * @return {Number} The sum
9886      */
9887     sum : function(property, start, end){
9888         var rs = this.data.items, v = 0;
9889         start = start || 0;
9890         end = (end || end === 0) ? end : rs.length-1;
9891
9892         for(var i = start; i <= end; i++){
9893             v += (rs[i].data[property] || 0);
9894         }
9895         return v;
9896     },
9897
9898     /**
9899      * Filter the records by a specified property.
9900      * @param {String} field A field on your records
9901      * @param {String/RegExp} value Either a string that the field
9902      * should start with or a RegExp to test against the field
9903      * @param {Boolean} anyMatch True to match any part not just the beginning
9904      */
9905     filter : function(property, value, anyMatch){
9906         var fn = this.createFilterFn(property, value, anyMatch);
9907         return fn ? this.filterBy(fn) : this.clearFilter();
9908     },
9909
9910     /**
9911      * Filter by a function. The specified function will be called with each
9912      * record in this data source. If the function returns true the record is included,
9913      * otherwise it is filtered.
9914      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9915      * @param {Object} scope (optional) The scope of the function (defaults to this)
9916      */
9917     filterBy : function(fn, scope){
9918         this.snapshot = this.snapshot || this.data;
9919         this.data = this.queryBy(fn, scope||this);
9920         this.fireEvent("datachanged", this);
9921     },
9922
9923     /**
9924      * Query the records by a specified property.
9925      * @param {String} field A field on your records
9926      * @param {String/RegExp} value Either a string that the field
9927      * should start with or a RegExp to test against the field
9928      * @param {Boolean} anyMatch True to match any part not just the beginning
9929      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9930      */
9931     query : function(property, value, anyMatch){
9932         var fn = this.createFilterFn(property, value, anyMatch);
9933         return fn ? this.queryBy(fn) : this.data.clone();
9934     },
9935
9936     /**
9937      * Query by a function. The specified function will be called with each
9938      * record in this data source. If the function returns true the record is included
9939      * in the results.
9940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9941      * @param {Object} scope (optional) The scope of the function (defaults to this)
9942       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9943      **/
9944     queryBy : function(fn, scope){
9945         var data = this.snapshot || this.data;
9946         return data.filterBy(fn, scope||this);
9947     },
9948
9949     /**
9950      * Collects unique values for a particular dataIndex from this store.
9951      * @param {String} dataIndex The property to collect
9952      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9953      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9954      * @return {Array} An array of the unique values
9955      **/
9956     collect : function(dataIndex, allowNull, bypassFilter){
9957         var d = (bypassFilter === true && this.snapshot) ?
9958                 this.snapshot.items : this.data.items;
9959         var v, sv, r = [], l = {};
9960         for(var i = 0, len = d.length; i < len; i++){
9961             v = d[i].data[dataIndex];
9962             sv = String(v);
9963             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9964                 l[sv] = true;
9965                 r[r.length] = v;
9966             }
9967         }
9968         return r;
9969     },
9970
9971     /**
9972      * Revert to a view of the Record cache with no filtering applied.
9973      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9974      */
9975     clearFilter : function(suppressEvent){
9976         if(this.snapshot && this.snapshot != this.data){
9977             this.data = this.snapshot;
9978             delete this.snapshot;
9979             if(suppressEvent !== true){
9980                 this.fireEvent("datachanged", this);
9981             }
9982         }
9983     },
9984
9985     // private
9986     afterEdit : function(record){
9987         if(this.modified.indexOf(record) == -1){
9988             this.modified.push(record);
9989         }
9990         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9991     },
9992     
9993     // private
9994     afterReject : function(record){
9995         this.modified.remove(record);
9996         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9997     },
9998
9999     // private
10000     afterCommit : function(record){
10001         this.modified.remove(record);
10002         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10003     },
10004
10005     /**
10006      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10007      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10008      */
10009     commitChanges : function(){
10010         var m = this.modified.slice(0);
10011         this.modified = [];
10012         for(var i = 0, len = m.length; i < len; i++){
10013             m[i].commit();
10014         }
10015     },
10016
10017     /**
10018      * Cancel outstanding changes on all changed records.
10019      */
10020     rejectChanges : function(){
10021         var m = this.modified.slice(0);
10022         this.modified = [];
10023         for(var i = 0, len = m.length; i < len; i++){
10024             m[i].reject();
10025         }
10026     },
10027
10028     onMetaChange : function(meta, rtype, o){
10029         this.recordType = rtype;
10030         this.fields = rtype.prototype.fields;
10031         delete this.snapshot;
10032         this.sortInfo = meta.sortInfo || this.sortInfo;
10033         this.modified = [];
10034         this.fireEvent('metachange', this, this.reader.meta);
10035     },
10036     
10037     moveIndex : function(data, type)
10038     {
10039         var index = this.indexOf(data);
10040         
10041         var newIndex = index + type;
10042         
10043         this.remove(data);
10044         
10045         this.insert(newIndex, data);
10046         
10047     }
10048 });/*
10049  * Based on:
10050  * Ext JS Library 1.1.1
10051  * Copyright(c) 2006-2007, Ext JS, LLC.
10052  *
10053  * Originally Released Under LGPL - original licence link has changed is not relivant.
10054  *
10055  * Fork - LGPL
10056  * <script type="text/javascript">
10057  */
10058
10059 /**
10060  * @class Roo.data.SimpleStore
10061  * @extends Roo.data.Store
10062  * Small helper class to make creating Stores from Array data easier.
10063  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10064  * @cfg {Array} fields An array of field definition objects, or field name strings.
10065  * @cfg {Array} data The multi-dimensional array of data
10066  * @constructor
10067  * @param {Object} config
10068  */
10069 Roo.data.SimpleStore = function(config){
10070     Roo.data.SimpleStore.superclass.constructor.call(this, {
10071         isLocal : true,
10072         reader: new Roo.data.ArrayReader({
10073                 id: config.id
10074             },
10075             Roo.data.Record.create(config.fields)
10076         ),
10077         proxy : new Roo.data.MemoryProxy(config.data)
10078     });
10079     this.load();
10080 };
10081 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10082  * Based on:
10083  * Ext JS Library 1.1.1
10084  * Copyright(c) 2006-2007, Ext JS, LLC.
10085  *
10086  * Originally Released Under LGPL - original licence link has changed is not relivant.
10087  *
10088  * Fork - LGPL
10089  * <script type="text/javascript">
10090  */
10091
10092 /**
10093 /**
10094  * @extends Roo.data.Store
10095  * @class Roo.data.JsonStore
10096  * Small helper class to make creating Stores for JSON data easier. <br/>
10097 <pre><code>
10098 var store = new Roo.data.JsonStore({
10099     url: 'get-images.php',
10100     root: 'images',
10101     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10102 });
10103 </code></pre>
10104  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10105  * JsonReader and HttpProxy (unless inline data is provided).</b>
10106  * @cfg {Array} fields An array of field definition objects, or field name strings.
10107  * @constructor
10108  * @param {Object} config
10109  */
10110 Roo.data.JsonStore = function(c){
10111     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10112         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10113         reader: new Roo.data.JsonReader(c, c.fields)
10114     }));
10115 };
10116 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10117  * Based on:
10118  * Ext JS Library 1.1.1
10119  * Copyright(c) 2006-2007, Ext JS, LLC.
10120  *
10121  * Originally Released Under LGPL - original licence link has changed is not relivant.
10122  *
10123  * Fork - LGPL
10124  * <script type="text/javascript">
10125  */
10126
10127  
10128 Roo.data.Field = function(config){
10129     if(typeof config == "string"){
10130         config = {name: config};
10131     }
10132     Roo.apply(this, config);
10133     
10134     if(!this.type){
10135         this.type = "auto";
10136     }
10137     
10138     var st = Roo.data.SortTypes;
10139     // named sortTypes are supported, here we look them up
10140     if(typeof this.sortType == "string"){
10141         this.sortType = st[this.sortType];
10142     }
10143     
10144     // set default sortType for strings and dates
10145     if(!this.sortType){
10146         switch(this.type){
10147             case "string":
10148                 this.sortType = st.asUCString;
10149                 break;
10150             case "date":
10151                 this.sortType = st.asDate;
10152                 break;
10153             default:
10154                 this.sortType = st.none;
10155         }
10156     }
10157
10158     // define once
10159     var stripRe = /[\$,%]/g;
10160
10161     // prebuilt conversion function for this field, instead of
10162     // switching every time we're reading a value
10163     if(!this.convert){
10164         var cv, dateFormat = this.dateFormat;
10165         switch(this.type){
10166             case "":
10167             case "auto":
10168             case undefined:
10169                 cv = function(v){ return v; };
10170                 break;
10171             case "string":
10172                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10173                 break;
10174             case "int":
10175                 cv = function(v){
10176                     return v !== undefined && v !== null && v !== '' ?
10177                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10178                     };
10179                 break;
10180             case "float":
10181                 cv = function(v){
10182                     return v !== undefined && v !== null && v !== '' ?
10183                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10184                     };
10185                 break;
10186             case "bool":
10187             case "boolean":
10188                 cv = function(v){ return v === true || v === "true" || v == 1; };
10189                 break;
10190             case "date":
10191                 cv = function(v){
10192                     if(!v){
10193                         return '';
10194                     }
10195                     if(v instanceof Date){
10196                         return v;
10197                     }
10198                     if(dateFormat){
10199                         if(dateFormat == "timestamp"){
10200                             return new Date(v*1000);
10201                         }
10202                         return Date.parseDate(v, dateFormat);
10203                     }
10204                     var parsed = Date.parse(v);
10205                     return parsed ? new Date(parsed) : null;
10206                 };
10207              break;
10208             
10209         }
10210         this.convert = cv;
10211     }
10212 };
10213
10214 Roo.data.Field.prototype = {
10215     dateFormat: null,
10216     defaultValue: "",
10217     mapping: null,
10218     sortType : null,
10219     sortDir : "ASC"
10220 };/*
10221  * Based on:
10222  * Ext JS Library 1.1.1
10223  * Copyright(c) 2006-2007, Ext JS, LLC.
10224  *
10225  * Originally Released Under LGPL - original licence link has changed is not relivant.
10226  *
10227  * Fork - LGPL
10228  * <script type="text/javascript">
10229  */
10230  
10231 // Base class for reading structured data from a data source.  This class is intended to be
10232 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10233
10234 /**
10235  * @class Roo.data.DataReader
10236  * Base class for reading structured data from a data source.  This class is intended to be
10237  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10238  */
10239
10240 Roo.data.DataReader = function(meta, recordType){
10241     
10242     this.meta = meta;
10243     
10244     this.recordType = recordType instanceof Array ? 
10245         Roo.data.Record.create(recordType) : recordType;
10246 };
10247
10248 Roo.data.DataReader.prototype = {
10249      /**
10250      * Create an empty record
10251      * @param {Object} data (optional) - overlay some values
10252      * @return {Roo.data.Record} record created.
10253      */
10254     newRow :  function(d) {
10255         var da =  {};
10256         this.recordType.prototype.fields.each(function(c) {
10257             switch( c.type) {
10258                 case 'int' : da[c.name] = 0; break;
10259                 case 'date' : da[c.name] = new Date(); break;
10260                 case 'float' : da[c.name] = 0.0; break;
10261                 case 'boolean' : da[c.name] = false; break;
10262                 default : da[c.name] = ""; break;
10263             }
10264             
10265         });
10266         return new this.recordType(Roo.apply(da, d));
10267     }
10268     
10269 };/*
10270  * Based on:
10271  * Ext JS Library 1.1.1
10272  * Copyright(c) 2006-2007, Ext JS, LLC.
10273  *
10274  * Originally Released Under LGPL - original licence link has changed is not relivant.
10275  *
10276  * Fork - LGPL
10277  * <script type="text/javascript">
10278  */
10279
10280 /**
10281  * @class Roo.data.DataProxy
10282  * @extends Roo.data.Observable
10283  * This class is an abstract base class for implementations which provide retrieval of
10284  * unformatted data objects.<br>
10285  * <p>
10286  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10287  * (of the appropriate type which knows how to parse the data object) to provide a block of
10288  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10289  * <p>
10290  * Custom implementations must implement the load method as described in
10291  * {@link Roo.data.HttpProxy#load}.
10292  */
10293 Roo.data.DataProxy = function(){
10294     this.addEvents({
10295         /**
10296          * @event beforeload
10297          * Fires before a network request is made to retrieve a data object.
10298          * @param {Object} This DataProxy object.
10299          * @param {Object} params The params parameter to the load function.
10300          */
10301         beforeload : true,
10302         /**
10303          * @event load
10304          * Fires before the load method's callback is called.
10305          * @param {Object} This DataProxy object.
10306          * @param {Object} o The data object.
10307          * @param {Object} arg The callback argument object passed to the load function.
10308          */
10309         load : true,
10310         /**
10311          * @event loadexception
10312          * Fires if an Exception occurs during data retrieval.
10313          * @param {Object} This DataProxy object.
10314          * @param {Object} o The data object.
10315          * @param {Object} arg The callback argument object passed to the load function.
10316          * @param {Object} e The Exception.
10317          */
10318         loadexception : true
10319     });
10320     Roo.data.DataProxy.superclass.constructor.call(this);
10321 };
10322
10323 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10324
10325     /**
10326      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10327      */
10328 /*
10329  * Based on:
10330  * Ext JS Library 1.1.1
10331  * Copyright(c) 2006-2007, Ext JS, LLC.
10332  *
10333  * Originally Released Under LGPL - original licence link has changed is not relivant.
10334  *
10335  * Fork - LGPL
10336  * <script type="text/javascript">
10337  */
10338 /**
10339  * @class Roo.data.MemoryProxy
10340  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10341  * to the Reader when its load method is called.
10342  * @constructor
10343  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10344  */
10345 Roo.data.MemoryProxy = function(data){
10346     if (data.data) {
10347         data = data.data;
10348     }
10349     Roo.data.MemoryProxy.superclass.constructor.call(this);
10350     this.data = data;
10351 };
10352
10353 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10354     /**
10355      * Load data from the requested source (in this case an in-memory
10356      * data object passed to the constructor), read the data object into
10357      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10358      * process that block using the passed callback.
10359      * @param {Object} params This parameter is not used by the MemoryProxy class.
10360      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10361      * object into a block of Roo.data.Records.
10362      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10363      * The function must be passed <ul>
10364      * <li>The Record block object</li>
10365      * <li>The "arg" argument from the load function</li>
10366      * <li>A boolean success indicator</li>
10367      * </ul>
10368      * @param {Object} scope The scope in which to call the callback
10369      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10370      */
10371     load : function(params, reader, callback, scope, arg){
10372         params = params || {};
10373         var result;
10374         try {
10375             result = reader.readRecords(this.data);
10376         }catch(e){
10377             this.fireEvent("loadexception", this, arg, null, e);
10378             callback.call(scope, null, arg, false);
10379             return;
10380         }
10381         callback.call(scope, result, arg, true);
10382     },
10383     
10384     // private
10385     update : function(params, records){
10386         
10387     }
10388 });/*
10389  * Based on:
10390  * Ext JS Library 1.1.1
10391  * Copyright(c) 2006-2007, Ext JS, LLC.
10392  *
10393  * Originally Released Under LGPL - original licence link has changed is not relivant.
10394  *
10395  * Fork - LGPL
10396  * <script type="text/javascript">
10397  */
10398 /**
10399  * @class Roo.data.HttpProxy
10400  * @extends Roo.data.DataProxy
10401  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10402  * configured to reference a certain URL.<br><br>
10403  * <p>
10404  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10405  * from which the running page was served.<br><br>
10406  * <p>
10407  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10408  * <p>
10409  * Be aware that to enable the browser to parse an XML document, the server must set
10410  * the Content-Type header in the HTTP response to "text/xml".
10411  * @constructor
10412  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10413  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10414  * will be used to make the request.
10415  */
10416 Roo.data.HttpProxy = function(conn){
10417     Roo.data.HttpProxy.superclass.constructor.call(this);
10418     // is conn a conn config or a real conn?
10419     this.conn = conn;
10420     this.useAjax = !conn || !conn.events;
10421   
10422 };
10423
10424 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10425     // thse are take from connection...
10426     
10427     /**
10428      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10429      */
10430     /**
10431      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10432      * extra parameters to each request made by this object. (defaults to undefined)
10433      */
10434     /**
10435      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10436      *  to each request made by this object. (defaults to undefined)
10437      */
10438     /**
10439      * @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)
10440      */
10441     /**
10442      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10443      */
10444      /**
10445      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10446      * @type Boolean
10447      */
10448   
10449
10450     /**
10451      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10452      * @type Boolean
10453      */
10454     /**
10455      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10456      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10457      * a finer-grained basis than the DataProxy events.
10458      */
10459     getConnection : function(){
10460         return this.useAjax ? Roo.Ajax : this.conn;
10461     },
10462
10463     /**
10464      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10465      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10466      * process that block using the passed callback.
10467      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10468      * for the request to the remote server.
10469      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10470      * object into a block of Roo.data.Records.
10471      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10472      * The function must be passed <ul>
10473      * <li>The Record block object</li>
10474      * <li>The "arg" argument from the load function</li>
10475      * <li>A boolean success indicator</li>
10476      * </ul>
10477      * @param {Object} scope The scope in which to call the callback
10478      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10479      */
10480     load : function(params, reader, callback, scope, arg){
10481         if(this.fireEvent("beforeload", this, params) !== false){
10482             var  o = {
10483                 params : params || {},
10484                 request: {
10485                     callback : callback,
10486                     scope : scope,
10487                     arg : arg
10488                 },
10489                 reader: reader,
10490                 callback : this.loadResponse,
10491                 scope: this
10492             };
10493             if(this.useAjax){
10494                 Roo.applyIf(o, this.conn);
10495                 if(this.activeRequest){
10496                     Roo.Ajax.abort(this.activeRequest);
10497                 }
10498                 this.activeRequest = Roo.Ajax.request(o);
10499             }else{
10500                 this.conn.request(o);
10501             }
10502         }else{
10503             callback.call(scope||this, null, arg, false);
10504         }
10505     },
10506
10507     // private
10508     loadResponse : function(o, success, response){
10509         delete this.activeRequest;
10510         if(!success){
10511             this.fireEvent("loadexception", this, o, response);
10512             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10513             return;
10514         }
10515         var result;
10516         try {
10517             result = o.reader.read(response);
10518         }catch(e){
10519             this.fireEvent("loadexception", this, o, response, e);
10520             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10521             return;
10522         }
10523         
10524         this.fireEvent("load", this, o, o.request.arg);
10525         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10526     },
10527
10528     // private
10529     update : function(dataSet){
10530
10531     },
10532
10533     // private
10534     updateResponse : function(dataSet){
10535
10536     }
10537 });/*
10538  * Based on:
10539  * Ext JS Library 1.1.1
10540  * Copyright(c) 2006-2007, Ext JS, LLC.
10541  *
10542  * Originally Released Under LGPL - original licence link has changed is not relivant.
10543  *
10544  * Fork - LGPL
10545  * <script type="text/javascript">
10546  */
10547
10548 /**
10549  * @class Roo.data.ScriptTagProxy
10550  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10551  * other than the originating domain of the running page.<br><br>
10552  * <p>
10553  * <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
10554  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10555  * <p>
10556  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10557  * source code that is used as the source inside a &lt;script> tag.<br><br>
10558  * <p>
10559  * In order for the browser to process the returned data, the server must wrap the data object
10560  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10561  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10562  * depending on whether the callback name was passed:
10563  * <p>
10564  * <pre><code>
10565 boolean scriptTag = false;
10566 String cb = request.getParameter("callback");
10567 if (cb != null) {
10568     scriptTag = true;
10569     response.setContentType("text/javascript");
10570 } else {
10571     response.setContentType("application/x-json");
10572 }
10573 Writer out = response.getWriter();
10574 if (scriptTag) {
10575     out.write(cb + "(");
10576 }
10577 out.print(dataBlock.toJsonString());
10578 if (scriptTag) {
10579     out.write(");");
10580 }
10581 </pre></code>
10582  *
10583  * @constructor
10584  * @param {Object} config A configuration object.
10585  */
10586 Roo.data.ScriptTagProxy = function(config){
10587     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10588     Roo.apply(this, config);
10589     this.head = document.getElementsByTagName("head")[0];
10590 };
10591
10592 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10593
10594 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10595     /**
10596      * @cfg {String} url The URL from which to request the data object.
10597      */
10598     /**
10599      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10600      */
10601     timeout : 30000,
10602     /**
10603      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10604      * the server the name of the callback function set up by the load call to process the returned data object.
10605      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10606      * javascript output which calls this named function passing the data object as its only parameter.
10607      */
10608     callbackParam : "callback",
10609     /**
10610      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10611      * name to the request.
10612      */
10613     nocache : true,
10614
10615     /**
10616      * Load data from the configured URL, read the data object into
10617      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10618      * process that block using the passed callback.
10619      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10620      * for the request to the remote server.
10621      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10622      * object into a block of Roo.data.Records.
10623      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10624      * The function must be passed <ul>
10625      * <li>The Record block object</li>
10626      * <li>The "arg" argument from the load function</li>
10627      * <li>A boolean success indicator</li>
10628      * </ul>
10629      * @param {Object} scope The scope in which to call the callback
10630      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10631      */
10632     load : function(params, reader, callback, scope, arg){
10633         if(this.fireEvent("beforeload", this, params) !== false){
10634
10635             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10636
10637             var url = this.url;
10638             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10639             if(this.nocache){
10640                 url += "&_dc=" + (new Date().getTime());
10641             }
10642             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10643             var trans = {
10644                 id : transId,
10645                 cb : "stcCallback"+transId,
10646                 scriptId : "stcScript"+transId,
10647                 params : params,
10648                 arg : arg,
10649                 url : url,
10650                 callback : callback,
10651                 scope : scope,
10652                 reader : reader
10653             };
10654             var conn = this;
10655
10656             window[trans.cb] = function(o){
10657                 conn.handleResponse(o, trans);
10658             };
10659
10660             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10661
10662             if(this.autoAbort !== false){
10663                 this.abort();
10664             }
10665
10666             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10667
10668             var script = document.createElement("script");
10669             script.setAttribute("src", url);
10670             script.setAttribute("type", "text/javascript");
10671             script.setAttribute("id", trans.scriptId);
10672             this.head.appendChild(script);
10673
10674             this.trans = trans;
10675         }else{
10676             callback.call(scope||this, null, arg, false);
10677         }
10678     },
10679
10680     // private
10681     isLoading : function(){
10682         return this.trans ? true : false;
10683     },
10684
10685     /**
10686      * Abort the current server request.
10687      */
10688     abort : function(){
10689         if(this.isLoading()){
10690             this.destroyTrans(this.trans);
10691         }
10692     },
10693
10694     // private
10695     destroyTrans : function(trans, isLoaded){
10696         this.head.removeChild(document.getElementById(trans.scriptId));
10697         clearTimeout(trans.timeoutId);
10698         if(isLoaded){
10699             window[trans.cb] = undefined;
10700             try{
10701                 delete window[trans.cb];
10702             }catch(e){}
10703         }else{
10704             // if hasn't been loaded, wait for load to remove it to prevent script error
10705             window[trans.cb] = function(){
10706                 window[trans.cb] = undefined;
10707                 try{
10708                     delete window[trans.cb];
10709                 }catch(e){}
10710             };
10711         }
10712     },
10713
10714     // private
10715     handleResponse : function(o, trans){
10716         this.trans = false;
10717         this.destroyTrans(trans, true);
10718         var result;
10719         try {
10720             result = trans.reader.readRecords(o);
10721         }catch(e){
10722             this.fireEvent("loadexception", this, o, trans.arg, e);
10723             trans.callback.call(trans.scope||window, null, trans.arg, false);
10724             return;
10725         }
10726         this.fireEvent("load", this, o, trans.arg);
10727         trans.callback.call(trans.scope||window, result, trans.arg, true);
10728     },
10729
10730     // private
10731     handleFailure : function(trans){
10732         this.trans = false;
10733         this.destroyTrans(trans, false);
10734         this.fireEvent("loadexception", this, null, trans.arg);
10735         trans.callback.call(trans.scope||window, null, trans.arg, false);
10736     }
10737 });/*
10738  * Based on:
10739  * Ext JS Library 1.1.1
10740  * Copyright(c) 2006-2007, Ext JS, LLC.
10741  *
10742  * Originally Released Under LGPL - original licence link has changed is not relivant.
10743  *
10744  * Fork - LGPL
10745  * <script type="text/javascript">
10746  */
10747
10748 /**
10749  * @class Roo.data.JsonReader
10750  * @extends Roo.data.DataReader
10751  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10752  * based on mappings in a provided Roo.data.Record constructor.
10753  * 
10754  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10755  * in the reply previously. 
10756  * 
10757  * <p>
10758  * Example code:
10759  * <pre><code>
10760 var RecordDef = Roo.data.Record.create([
10761     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10762     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10763 ]);
10764 var myReader = new Roo.data.JsonReader({
10765     totalProperty: "results",    // The property which contains the total dataset size (optional)
10766     root: "rows",                // The property which contains an Array of row objects
10767     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10768 }, RecordDef);
10769 </code></pre>
10770  * <p>
10771  * This would consume a JSON file like this:
10772  * <pre><code>
10773 { 'results': 2, 'rows': [
10774     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10775     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10776 }
10777 </code></pre>
10778  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10779  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10780  * paged from the remote server.
10781  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10782  * @cfg {String} root name of the property which contains the Array of row objects.
10783  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10784  * @cfg {Array} fields Array of field definition objects
10785  * @constructor
10786  * Create a new JsonReader
10787  * @param {Object} meta Metadata configuration options
10788  * @param {Object} recordType Either an Array of field definition objects,
10789  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10790  */
10791 Roo.data.JsonReader = function(meta, recordType){
10792     
10793     meta = meta || {};
10794     // set some defaults:
10795     Roo.applyIf(meta, {
10796         totalProperty: 'total',
10797         successProperty : 'success',
10798         root : 'data',
10799         id : 'id'
10800     });
10801     
10802     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10803 };
10804 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10805     
10806     /**
10807      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10808      * Used by Store query builder to append _requestMeta to params.
10809      * 
10810      */
10811     metaFromRemote : false,
10812     /**
10813      * This method is only used by a DataProxy which has retrieved data from a remote server.
10814      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10815      * @return {Object} data A data block which is used by an Roo.data.Store object as
10816      * a cache of Roo.data.Records.
10817      */
10818     read : function(response){
10819         var json = response.responseText;
10820        
10821         var o = /* eval:var:o */ eval("("+json+")");
10822         if(!o) {
10823             throw {message: "JsonReader.read: Json object not found"};
10824         }
10825         
10826         if(o.metaData){
10827             
10828             delete this.ef;
10829             this.metaFromRemote = true;
10830             this.meta = o.metaData;
10831             this.recordType = Roo.data.Record.create(o.metaData.fields);
10832             this.onMetaChange(this.meta, this.recordType, o);
10833         }
10834         return this.readRecords(o);
10835     },
10836
10837     // private function a store will implement
10838     onMetaChange : function(meta, recordType, o){
10839
10840     },
10841
10842     /**
10843          * @ignore
10844          */
10845     simpleAccess: function(obj, subsc) {
10846         return obj[subsc];
10847     },
10848
10849         /**
10850          * @ignore
10851          */
10852     getJsonAccessor: function(){
10853         var re = /[\[\.]/;
10854         return function(expr) {
10855             try {
10856                 return(re.test(expr))
10857                     ? new Function("obj", "return obj." + expr)
10858                     : function(obj){
10859                         return obj[expr];
10860                     };
10861             } catch(e){}
10862             return Roo.emptyFn;
10863         };
10864     }(),
10865
10866     /**
10867      * Create a data block containing Roo.data.Records from an XML document.
10868      * @param {Object} o An object which contains an Array of row objects in the property specified
10869      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10870      * which contains the total size of the dataset.
10871      * @return {Object} data A data block which is used by an Roo.data.Store object as
10872      * a cache of Roo.data.Records.
10873      */
10874     readRecords : function(o){
10875         /**
10876          * After any data loads, the raw JSON data is available for further custom processing.
10877          * @type Object
10878          */
10879         this.o = o;
10880         var s = this.meta, Record = this.recordType,
10881             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10882
10883 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10884         if (!this.ef) {
10885             if(s.totalProperty) {
10886                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10887                 }
10888                 if(s.successProperty) {
10889                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10890                 }
10891                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10892                 if (s.id) {
10893                         var g = this.getJsonAccessor(s.id);
10894                         this.getId = function(rec) {
10895                                 var r = g(rec);  
10896                                 return (r === undefined || r === "") ? null : r;
10897                         };
10898                 } else {
10899                         this.getId = function(){return null;};
10900                 }
10901             this.ef = [];
10902             for(var jj = 0; jj < fl; jj++){
10903                 f = fi[jj];
10904                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10905                 this.ef[jj] = this.getJsonAccessor(map);
10906             }
10907         }
10908
10909         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10910         if(s.totalProperty){
10911             var vt = parseInt(this.getTotal(o), 10);
10912             if(!isNaN(vt)){
10913                 totalRecords = vt;
10914             }
10915         }
10916         if(s.successProperty){
10917             var vs = this.getSuccess(o);
10918             if(vs === false || vs === 'false'){
10919                 success = false;
10920             }
10921         }
10922         var records = [];
10923         for(var i = 0; i < c; i++){
10924                 var n = root[i];
10925             var values = {};
10926             var id = this.getId(n);
10927             for(var j = 0; j < fl; j++){
10928                 f = fi[j];
10929             var v = this.ef[j](n);
10930             if (!f.convert) {
10931                 Roo.log('missing convert for ' + f.name);
10932                 Roo.log(f);
10933                 continue;
10934             }
10935             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10936             }
10937             var record = new Record(values, id);
10938             record.json = n;
10939             records[i] = record;
10940         }
10941         return {
10942             raw : o,
10943             success : success,
10944             records : records,
10945             totalRecords : totalRecords
10946         };
10947     }
10948 });/*
10949  * Based on:
10950  * Ext JS Library 1.1.1
10951  * Copyright(c) 2006-2007, Ext JS, LLC.
10952  *
10953  * Originally Released Under LGPL - original licence link has changed is not relivant.
10954  *
10955  * Fork - LGPL
10956  * <script type="text/javascript">
10957  */
10958
10959 /**
10960  * @class Roo.data.ArrayReader
10961  * @extends Roo.data.DataReader
10962  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10963  * Each element of that Array represents a row of data fields. The
10964  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10965  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10966  * <p>
10967  * Example code:.
10968  * <pre><code>
10969 var RecordDef = Roo.data.Record.create([
10970     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10971     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10972 ]);
10973 var myReader = new Roo.data.ArrayReader({
10974     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10975 }, RecordDef);
10976 </code></pre>
10977  * <p>
10978  * This would consume an Array like this:
10979  * <pre><code>
10980 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10981   </code></pre>
10982  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10983  * @constructor
10984  * Create a new JsonReader
10985  * @param {Object} meta Metadata configuration options.
10986  * @param {Object} recordType Either an Array of field definition objects
10987  * as specified to {@link Roo.data.Record#create},
10988  * or an {@link Roo.data.Record} object
10989  * created using {@link Roo.data.Record#create}.
10990  */
10991 Roo.data.ArrayReader = function(meta, recordType){
10992     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10993 };
10994
10995 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10996     /**
10997      * Create a data block containing Roo.data.Records from an XML document.
10998      * @param {Object} o An Array of row objects which represents the dataset.
10999      * @return {Object} data A data block which is used by an Roo.data.Store object as
11000      * a cache of Roo.data.Records.
11001      */
11002     readRecords : function(o){
11003         var sid = this.meta ? this.meta.id : null;
11004         var recordType = this.recordType, fields = recordType.prototype.fields;
11005         var records = [];
11006         var root = o;
11007             for(var i = 0; i < root.length; i++){
11008                     var n = root[i];
11009                 var values = {};
11010                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11011                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11012                 var f = fields.items[j];
11013                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11014                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11015                 v = f.convert(v);
11016                 values[f.name] = v;
11017             }
11018                 var record = new recordType(values, id);
11019                 record.json = n;
11020                 records[records.length] = record;
11021             }
11022             return {
11023                 records : records,
11024                 totalRecords : records.length
11025             };
11026     }
11027 });/*
11028  * - LGPL
11029  * * 
11030  */
11031
11032 /**
11033  * @class Roo.bootstrap.ComboBox
11034  * @extends Roo.bootstrap.TriggerField
11035  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11036  * @cfg {Boolean} append (true|false) default false
11037  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11038  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11039  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11040  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11041  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11042  * @cfg {Boolean} animate default true
11043  * @cfg {Boolean} emptyResultText only for touch device
11044  * @constructor
11045  * Create a new ComboBox.
11046  * @param {Object} config Configuration options
11047  */
11048 Roo.bootstrap.ComboBox = function(config){
11049     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11050     this.addEvents({
11051         /**
11052          * @event expand
11053          * Fires when the dropdown list is expanded
11054              * @param {Roo.bootstrap.ComboBox} combo This combo box
11055              */
11056         'expand' : true,
11057         /**
11058          * @event collapse
11059          * Fires when the dropdown list is collapsed
11060              * @param {Roo.bootstrap.ComboBox} combo This combo box
11061              */
11062         'collapse' : true,
11063         /**
11064          * @event beforeselect
11065          * Fires before a list item is selected. Return false to cancel the selection.
11066              * @param {Roo.bootstrap.ComboBox} combo This combo box
11067              * @param {Roo.data.Record} record The data record returned from the underlying store
11068              * @param {Number} index The index of the selected item in the dropdown list
11069              */
11070         'beforeselect' : true,
11071         /**
11072          * @event select
11073          * Fires when a list item is selected
11074              * @param {Roo.bootstrap.ComboBox} combo This combo box
11075              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11076              * @param {Number} index The index of the selected item in the dropdown list
11077              */
11078         'select' : true,
11079         /**
11080          * @event beforequery
11081          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11082          * The event object passed has these properties:
11083              * @param {Roo.bootstrap.ComboBox} combo This combo box
11084              * @param {String} query The query
11085              * @param {Boolean} forceAll true to force "all" query
11086              * @param {Boolean} cancel true to cancel the query
11087              * @param {Object} e The query event object
11088              */
11089         'beforequery': true,
11090          /**
11091          * @event add
11092          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11093              * @param {Roo.bootstrap.ComboBox} combo This combo box
11094              */
11095         'add' : true,
11096         /**
11097          * @event edit
11098          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11099              * @param {Roo.bootstrap.ComboBox} combo This combo box
11100              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11101              */
11102         'edit' : true,
11103         /**
11104          * @event remove
11105          * Fires when the remove value from the combobox array
11106              * @param {Roo.bootstrap.ComboBox} combo This combo box
11107              */
11108         'remove' : true,
11109         /**
11110          * @event specialfilter
11111          * Fires when specialfilter
11112             * @param {Roo.bootstrap.ComboBox} combo This combo box
11113             */
11114         'specialfilter' : true
11115         
11116     });
11117     
11118     this.item = [];
11119     this.tickItems = [];
11120     
11121     this.selectedIndex = -1;
11122     if(this.mode == 'local'){
11123         if(config.queryDelay === undefined){
11124             this.queryDelay = 10;
11125         }
11126         if(config.minChars === undefined){
11127             this.minChars = 0;
11128         }
11129     }
11130 };
11131
11132 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11133      
11134     /**
11135      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11136      * rendering into an Roo.Editor, defaults to false)
11137      */
11138     /**
11139      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11140      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11141      */
11142     /**
11143      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11144      */
11145     /**
11146      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11147      * the dropdown list (defaults to undefined, with no header element)
11148      */
11149
11150      /**
11151      * @cfg {String/Roo.Template} tpl The template to use to render the output
11152      */
11153      
11154      /**
11155      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11156      */
11157     listWidth: undefined,
11158     /**
11159      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11160      * mode = 'remote' or 'text' if mode = 'local')
11161      */
11162     displayField: undefined,
11163     
11164     /**
11165      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11166      * mode = 'remote' or 'value' if mode = 'local'). 
11167      * Note: use of a valueField requires the user make a selection
11168      * in order for a value to be mapped.
11169      */
11170     valueField: undefined,
11171     
11172     
11173     /**
11174      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11175      * field's data value (defaults to the underlying DOM element's name)
11176      */
11177     hiddenName: undefined,
11178     /**
11179      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11180      */
11181     listClass: '',
11182     /**
11183      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11184      */
11185     selectedClass: 'active',
11186     
11187     /**
11188      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11189      */
11190     shadow:'sides',
11191     /**
11192      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11193      * anchor positions (defaults to 'tl-bl')
11194      */
11195     listAlign: 'tl-bl?',
11196     /**
11197      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11198      */
11199     maxHeight: 300,
11200     /**
11201      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11202      * query specified by the allQuery config option (defaults to 'query')
11203      */
11204     triggerAction: 'query',
11205     /**
11206      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11207      * (defaults to 4, does not apply if editable = false)
11208      */
11209     minChars : 4,
11210     /**
11211      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11212      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11213      */
11214     typeAhead: false,
11215     /**
11216      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11217      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11218      */
11219     queryDelay: 500,
11220     /**
11221      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11222      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11223      */
11224     pageSize: 0,
11225     /**
11226      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11227      * when editable = true (defaults to false)
11228      */
11229     selectOnFocus:false,
11230     /**
11231      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11232      */
11233     queryParam: 'query',
11234     /**
11235      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11236      * when mode = 'remote' (defaults to 'Loading...')
11237      */
11238     loadingText: 'Loading...',
11239     /**
11240      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11241      */
11242     resizable: false,
11243     /**
11244      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11245      */
11246     handleHeight : 8,
11247     /**
11248      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11249      * traditional select (defaults to true)
11250      */
11251     editable: true,
11252     /**
11253      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11254      */
11255     allQuery: '',
11256     /**
11257      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11258      */
11259     mode: 'remote',
11260     /**
11261      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11262      * listWidth has a higher value)
11263      */
11264     minListWidth : 70,
11265     /**
11266      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11267      * allow the user to set arbitrary text into the field (defaults to false)
11268      */
11269     forceSelection:false,
11270     /**
11271      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11272      * if typeAhead = true (defaults to 250)
11273      */
11274     typeAheadDelay : 250,
11275     /**
11276      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11277      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11278      */
11279     valueNotFoundText : undefined,
11280     /**
11281      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11282      */
11283     blockFocus : false,
11284     
11285     /**
11286      * @cfg {Boolean} disableClear Disable showing of clear button.
11287      */
11288     disableClear : false,
11289     /**
11290      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11291      */
11292     alwaysQuery : false,
11293     
11294     /**
11295      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11296      */
11297     multiple : false,
11298     
11299     /**
11300      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11301      */
11302     invalidClass : "has-warning",
11303     
11304     /**
11305      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11306      */
11307     validClass : "has-success",
11308     
11309     /**
11310      * @cfg {Boolean} specialFilter (true|false) special filter default false
11311      */
11312     specialFilter : false,
11313     
11314     /**
11315      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11316      */
11317     mobileTouchView : true,
11318     
11319     //private
11320     addicon : false,
11321     editicon: false,
11322     
11323     page: 0,
11324     hasQuery: false,
11325     append: false,
11326     loadNext: false,
11327     autoFocus : true,
11328     tickable : false,
11329     btnPosition : 'right',
11330     triggerList : true,
11331     showToggleBtn : true,
11332     animate : true,
11333     emptyResultText: 'Empty',
11334     // element that contains real text value.. (when hidden is used..)
11335     
11336     getAutoCreate : function()
11337     {
11338         var cfg = false;
11339         
11340         /*
11341          * Touch Devices
11342          */
11343         
11344         if(Roo.isTouch && this.mobileTouchView){
11345             cfg = this.getAutoCreateTouchView();
11346             return cfg;;
11347         }
11348         
11349         /*
11350          *  Normal ComboBox
11351          */
11352         if(!this.tickable){
11353             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11354             return cfg;
11355         }
11356         
11357         /*
11358          *  ComboBox with tickable selections
11359          */
11360              
11361         var align = this.labelAlign || this.parentLabelAlign();
11362         
11363         cfg = {
11364             cls : 'form-group roo-combobox-tickable' //input-group
11365         };
11366         
11367         var buttons = {
11368             tag : 'div',
11369             cls : 'tickable-buttons',
11370             cn : [
11371                 {
11372                     tag : 'button',
11373                     type : 'button',
11374                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11375                     html : 'Edit'
11376                 },
11377                 {
11378                     tag : 'button',
11379                     type : 'button',
11380                     name : 'ok',
11381                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11382                     html : 'Done'
11383                 },
11384                 {
11385                     tag : 'button',
11386                     type : 'button',
11387                     name : 'cancel',
11388                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11389                     html : 'Cancel'
11390                 }
11391             ]
11392         };
11393         
11394         if(this.editable){
11395             buttons.cn.unshift({
11396                 tag: 'input',
11397                 cls: 'select2-search-field-input'
11398             });
11399         }
11400         
11401         var _this = this;
11402         
11403         Roo.each(buttons.cn, function(c){
11404             if (_this.size) {
11405                 c.cls += ' btn-' + _this.size;
11406             }
11407
11408             if (_this.disabled) {
11409                 c.disabled = true;
11410             }
11411         });
11412         
11413         var box = {
11414             tag: 'div',
11415             cn: [
11416                 {
11417                     tag: 'input',
11418                     type : 'hidden',
11419                     cls: 'form-hidden-field'
11420                 },
11421                 {
11422                     tag: 'ul',
11423                     cls: 'select2-choices',
11424                     cn:[
11425                         {
11426                             tag: 'li',
11427                             cls: 'select2-search-field',
11428                             cn: [
11429
11430                                 buttons
11431                             ]
11432                         }
11433                     ]
11434                 }
11435             ]
11436         }
11437         
11438         var combobox = {
11439             cls: 'select2-container input-group select2-container-multi',
11440             cn: [
11441                 box
11442 //                {
11443 //                    tag: 'ul',
11444 //                    cls: 'typeahead typeahead-long dropdown-menu',
11445 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11446 //                }
11447             ]
11448         };
11449         
11450         if(this.hasFeedback && !this.allowBlank){
11451             
11452             var feedback = {
11453                 tag: 'span',
11454                 cls: 'glyphicon form-control-feedback'
11455             };
11456
11457             combobox.cn.push(feedback);
11458         }
11459         
11460         if (align ==='left' && this.fieldLabel.length) {
11461             
11462                 Roo.log("left and has label");
11463                 cfg.cn = [
11464                     
11465                     {
11466                         tag: 'label',
11467                         'for' :  id,
11468                         cls : 'control-label col-sm-' + this.labelWidth,
11469                         html : this.fieldLabel
11470                         
11471                     },
11472                     {
11473                         cls : "col-sm-" + (12 - this.labelWidth), 
11474                         cn: [
11475                             combobox
11476                         ]
11477                     }
11478                     
11479                 ];
11480         } else if ( this.fieldLabel.length) {
11481                 Roo.log(" label");
11482                  cfg.cn = [
11483                    
11484                     {
11485                         tag: 'label',
11486                         //cls : 'input-group-addon',
11487                         html : this.fieldLabel
11488                         
11489                     },
11490                     
11491                     combobox
11492                     
11493                 ];
11494
11495         } else {
11496             
11497                 Roo.log(" no label && no align");
11498                 cfg = combobox
11499                      
11500                 
11501         }
11502          
11503         var settings=this;
11504         ['xs','sm','md','lg'].map(function(size){
11505             if (settings[size]) {
11506                 cfg.cls += ' col-' + size + '-' + settings[size];
11507             }
11508         });
11509         
11510         return cfg;
11511         
11512     },
11513     
11514     // private
11515     initEvents: function()
11516     {
11517         
11518         if (!this.store) {
11519             throw "can not find store for combo";
11520         }
11521         
11522         this.store = Roo.factory(this.store, Roo.data);
11523         
11524         /*
11525          * Touch Devices
11526          */
11527         
11528         if(Roo.isTouch && this.mobileTouchView){
11529             this.initTouchView();
11530             return;
11531         }
11532         
11533         if(this.tickable){
11534             this.initTickableEvents();
11535             return;
11536         }
11537         
11538         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11539         
11540         if(this.hiddenName){
11541             
11542             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11543             
11544             this.hiddenField.dom.value =
11545                 this.hiddenValue !== undefined ? this.hiddenValue :
11546                 this.value !== undefined ? this.value : '';
11547
11548             // prevent input submission
11549             this.el.dom.removeAttribute('name');
11550             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11551              
11552              
11553         }
11554         //if(Roo.isGecko){
11555         //    this.el.dom.setAttribute('autocomplete', 'off');
11556         //}
11557         
11558         var cls = 'x-combo-list';
11559         
11560         //this.list = new Roo.Layer({
11561         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11562         //});
11563         
11564         var _this = this;
11565         
11566         (function(){
11567             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11568             _this.list.setWidth(lw);
11569         }).defer(100);
11570         
11571         this.list.on('mouseover', this.onViewOver, this);
11572         this.list.on('mousemove', this.onViewMove, this);
11573         
11574         this.list.on('scroll', this.onViewScroll, this);
11575         
11576         /*
11577         this.list.swallowEvent('mousewheel');
11578         this.assetHeight = 0;
11579
11580         if(this.title){
11581             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11582             this.assetHeight += this.header.getHeight();
11583         }
11584
11585         this.innerList = this.list.createChild({cls:cls+'-inner'});
11586         this.innerList.on('mouseover', this.onViewOver, this);
11587         this.innerList.on('mousemove', this.onViewMove, this);
11588         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11589         
11590         if(this.allowBlank && !this.pageSize && !this.disableClear){
11591             this.footer = this.list.createChild({cls:cls+'-ft'});
11592             this.pageTb = new Roo.Toolbar(this.footer);
11593            
11594         }
11595         if(this.pageSize){
11596             this.footer = this.list.createChild({cls:cls+'-ft'});
11597             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11598                     {pageSize: this.pageSize});
11599             
11600         }
11601         
11602         if (this.pageTb && this.allowBlank && !this.disableClear) {
11603             var _this = this;
11604             this.pageTb.add(new Roo.Toolbar.Fill(), {
11605                 cls: 'x-btn-icon x-btn-clear',
11606                 text: '&#160;',
11607                 handler: function()
11608                 {
11609                     _this.collapse();
11610                     _this.clearValue();
11611                     _this.onSelect(false, -1);
11612                 }
11613             });
11614         }
11615         if (this.footer) {
11616             this.assetHeight += this.footer.getHeight();
11617         }
11618         */
11619             
11620         if(!this.tpl){
11621             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11622         }
11623
11624         this.view = new Roo.View(this.list, this.tpl, {
11625             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11626         });
11627         //this.view.wrapEl.setDisplayed(false);
11628         this.view.on('click', this.onViewClick, this);
11629         
11630         
11631         
11632         this.store.on('beforeload', this.onBeforeLoad, this);
11633         this.store.on('load', this.onLoad, this);
11634         this.store.on('loadexception', this.onLoadException, this);
11635         /*
11636         if(this.resizable){
11637             this.resizer = new Roo.Resizable(this.list,  {
11638                pinned:true, handles:'se'
11639             });
11640             this.resizer.on('resize', function(r, w, h){
11641                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11642                 this.listWidth = w;
11643                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11644                 this.restrictHeight();
11645             }, this);
11646             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11647         }
11648         */
11649         if(!this.editable){
11650             this.editable = true;
11651             this.setEditable(false);
11652         }
11653         
11654         /*
11655         
11656         if (typeof(this.events.add.listeners) != 'undefined') {
11657             
11658             this.addicon = this.wrap.createChild(
11659                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11660        
11661             this.addicon.on('click', function(e) {
11662                 this.fireEvent('add', this);
11663             }, this);
11664         }
11665         if (typeof(this.events.edit.listeners) != 'undefined') {
11666             
11667             this.editicon = this.wrap.createChild(
11668                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11669             if (this.addicon) {
11670                 this.editicon.setStyle('margin-left', '40px');
11671             }
11672             this.editicon.on('click', function(e) {
11673                 
11674                 // we fire even  if inothing is selected..
11675                 this.fireEvent('edit', this, this.lastData );
11676                 
11677             }, this);
11678         }
11679         */
11680         
11681         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11682             "up" : function(e){
11683                 this.inKeyMode = true;
11684                 this.selectPrev();
11685             },
11686
11687             "down" : function(e){
11688                 if(!this.isExpanded()){
11689                     this.onTriggerClick();
11690                 }else{
11691                     this.inKeyMode = true;
11692                     this.selectNext();
11693                 }
11694             },
11695
11696             "enter" : function(e){
11697 //                this.onViewClick();
11698                 //return true;
11699                 this.collapse();
11700                 
11701                 if(this.fireEvent("specialkey", this, e)){
11702                     this.onViewClick(false);
11703                 }
11704                 
11705                 return true;
11706             },
11707
11708             "esc" : function(e){
11709                 this.collapse();
11710             },
11711
11712             "tab" : function(e){
11713                 this.collapse();
11714                 
11715                 if(this.fireEvent("specialkey", this, e)){
11716                     this.onViewClick(false);
11717                 }
11718                 
11719                 return true;
11720             },
11721
11722             scope : this,
11723
11724             doRelay : function(foo, bar, hname){
11725                 if(hname == 'down' || this.scope.isExpanded()){
11726                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11727                 }
11728                 return true;
11729             },
11730
11731             forceKeyDown: true
11732         });
11733         
11734         
11735         this.queryDelay = Math.max(this.queryDelay || 10,
11736                 this.mode == 'local' ? 10 : 250);
11737         
11738         
11739         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11740         
11741         if(this.typeAhead){
11742             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11743         }
11744         if(this.editable !== false){
11745             this.inputEl().on("keyup", this.onKeyUp, this);
11746         }
11747         if(this.forceSelection){
11748             this.inputEl().on('blur', this.doForce, this);
11749         }
11750         
11751         if(this.multiple){
11752             this.choices = this.el.select('ul.select2-choices', true).first();
11753             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11754         }
11755     },
11756     
11757     initTickableEvents: function()
11758     {   
11759         this.createList();
11760         
11761         if(this.hiddenName){
11762             
11763             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11764             
11765             this.hiddenField.dom.value =
11766                 this.hiddenValue !== undefined ? this.hiddenValue :
11767                 this.value !== undefined ? this.value : '';
11768
11769             // prevent input submission
11770             this.el.dom.removeAttribute('name');
11771             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11772              
11773              
11774         }
11775         
11776 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11777         
11778         this.choices = this.el.select('ul.select2-choices', true).first();
11779         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11780         if(this.triggerList){
11781             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11782         }
11783          
11784         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11785         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11786         
11787         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11788         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11789         
11790         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11791         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11792         
11793         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11794         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11795         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11796         
11797         this.okBtn.hide();
11798         this.cancelBtn.hide();
11799         
11800         var _this = this;
11801         
11802         (function(){
11803             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11804             _this.list.setWidth(lw);
11805         }).defer(100);
11806         
11807         this.list.on('mouseover', this.onViewOver, this);
11808         this.list.on('mousemove', this.onViewMove, this);
11809         
11810         this.list.on('scroll', this.onViewScroll, this);
11811         
11812         if(!this.tpl){
11813             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>';
11814         }
11815
11816         this.view = new Roo.View(this.list, this.tpl, {
11817             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11818         });
11819         
11820         //this.view.wrapEl.setDisplayed(false);
11821         this.view.on('click', this.onViewClick, this);
11822         
11823         
11824         
11825         this.store.on('beforeload', this.onBeforeLoad, this);
11826         this.store.on('load', this.onLoad, this);
11827         this.store.on('loadexception', this.onLoadException, this);
11828         
11829         if(this.editable){
11830             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11831                 "up" : function(e){
11832                     this.inKeyMode = true;
11833                     this.selectPrev();
11834                 },
11835
11836                 "down" : function(e){
11837                     this.inKeyMode = true;
11838                     this.selectNext();
11839                 },
11840
11841                 "enter" : function(e){
11842                     if(this.fireEvent("specialkey", this, e)){
11843                         this.onViewClick(false);
11844                     }
11845                     
11846                     return true;
11847                 },
11848
11849                 "esc" : function(e){
11850                     this.onTickableFooterButtonClick(e, false, false);
11851                 },
11852
11853                 "tab" : function(e){
11854                     this.fireEvent("specialkey", this, e);
11855                     
11856                     this.onTickableFooterButtonClick(e, false, false);
11857                     
11858                     return true;
11859                 },
11860
11861                 scope : this,
11862
11863                 doRelay : function(e, fn, key){
11864                     if(this.scope.isExpanded()){
11865                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11866                     }
11867                     return true;
11868                 },
11869
11870                 forceKeyDown: true
11871             });
11872         }
11873         
11874         this.queryDelay = Math.max(this.queryDelay || 10,
11875                 this.mode == 'local' ? 10 : 250);
11876         
11877         
11878         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11879         
11880         if(this.typeAhead){
11881             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11882         }
11883         
11884         if(this.editable !== false){
11885             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11886         }
11887         
11888     },
11889
11890     onDestroy : function(){
11891         if(this.view){
11892             this.view.setStore(null);
11893             this.view.el.removeAllListeners();
11894             this.view.el.remove();
11895             this.view.purgeListeners();
11896         }
11897         if(this.list){
11898             this.list.dom.innerHTML  = '';
11899         }
11900         
11901         if(this.store){
11902             this.store.un('beforeload', this.onBeforeLoad, this);
11903             this.store.un('load', this.onLoad, this);
11904             this.store.un('loadexception', this.onLoadException, this);
11905         }
11906         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11907     },
11908
11909     // private
11910     fireKey : function(e){
11911         if(e.isNavKeyPress() && !this.list.isVisible()){
11912             this.fireEvent("specialkey", this, e);
11913         }
11914     },
11915
11916     // private
11917     onResize: function(w, h){
11918 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11919 //        
11920 //        if(typeof w != 'number'){
11921 //            // we do not handle it!?!?
11922 //            return;
11923 //        }
11924 //        var tw = this.trigger.getWidth();
11925 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11926 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11927 //        var x = w - tw;
11928 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11929 //            
11930 //        //this.trigger.setStyle('left', x+'px');
11931 //        
11932 //        if(this.list && this.listWidth === undefined){
11933 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11934 //            this.list.setWidth(lw);
11935 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11936 //        }
11937         
11938     
11939         
11940     },
11941
11942     /**
11943      * Allow or prevent the user from directly editing the field text.  If false is passed,
11944      * the user will only be able to select from the items defined in the dropdown list.  This method
11945      * is the runtime equivalent of setting the 'editable' config option at config time.
11946      * @param {Boolean} value True to allow the user to directly edit the field text
11947      */
11948     setEditable : function(value){
11949         if(value == this.editable){
11950             return;
11951         }
11952         this.editable = value;
11953         if(!value){
11954             this.inputEl().dom.setAttribute('readOnly', true);
11955             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11956             this.inputEl().addClass('x-combo-noedit');
11957         }else{
11958             this.inputEl().dom.setAttribute('readOnly', false);
11959             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11960             this.inputEl().removeClass('x-combo-noedit');
11961         }
11962     },
11963
11964     // private
11965     
11966     onBeforeLoad : function(combo,opts){
11967         if(!this.hasFocus){
11968             return;
11969         }
11970          if (!opts.add) {
11971             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11972          }
11973         this.restrictHeight();
11974         this.selectedIndex = -1;
11975     },
11976
11977     // private
11978     onLoad : function(){
11979         
11980         this.hasQuery = false;
11981         
11982         if(!this.hasFocus){
11983             return;
11984         }
11985         
11986         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11987             this.loading.hide();
11988         }
11989              
11990         if(this.store.getCount() > 0){
11991             this.expand();
11992             this.restrictHeight();
11993             if(this.lastQuery == this.allQuery){
11994                 if(this.editable && !this.tickable){
11995                     this.inputEl().dom.select();
11996                 }
11997                 
11998                 if(
11999                     !this.selectByValue(this.value, true) &&
12000                     this.autoFocus && 
12001                     (
12002                         !this.store.lastOptions ||
12003                         typeof(this.store.lastOptions.add) == 'undefined' || 
12004                         this.store.lastOptions.add != true
12005                     )
12006                 ){
12007                     this.select(0, true);
12008                 }
12009             }else{
12010                 if(this.autoFocus){
12011                     this.selectNext();
12012                 }
12013                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12014                     this.taTask.delay(this.typeAheadDelay);
12015                 }
12016             }
12017         }else{
12018             this.onEmptyResults();
12019         }
12020         
12021         //this.el.focus();
12022     },
12023     // private
12024     onLoadException : function()
12025     {
12026         this.hasQuery = false;
12027         
12028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12029             this.loading.hide();
12030         }
12031         
12032         if(this.tickable && this.editable){
12033             return;
12034         }
12035         
12036         this.collapse();
12037         
12038         Roo.log(this.store.reader.jsonData);
12039         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12040             // fixme
12041             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12042         }
12043         
12044         
12045     },
12046     // private
12047     onTypeAhead : function(){
12048         if(this.store.getCount() > 0){
12049             var r = this.store.getAt(0);
12050             var newValue = r.data[this.displayField];
12051             var len = newValue.length;
12052             var selStart = this.getRawValue().length;
12053             
12054             if(selStart != len){
12055                 this.setRawValue(newValue);
12056                 this.selectText(selStart, newValue.length);
12057             }
12058         }
12059     },
12060
12061     // private
12062     onSelect : function(record, index){
12063         
12064         if(this.fireEvent('beforeselect', this, record, index) !== false){
12065         
12066             this.setFromData(index > -1 ? record.data : false);
12067             
12068             this.collapse();
12069             this.fireEvent('select', this, record, index);
12070         }
12071     },
12072
12073     /**
12074      * Returns the currently selected field value or empty string if no value is set.
12075      * @return {String} value The selected value
12076      */
12077     getValue : function(){
12078         
12079         if(this.multiple){
12080             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12081         }
12082         
12083         if(this.valueField){
12084             return typeof this.value != 'undefined' ? this.value : '';
12085         }else{
12086             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12087         }
12088     },
12089
12090     /**
12091      * Clears any text/value currently set in the field
12092      */
12093     clearValue : function(){
12094         if(this.hiddenField){
12095             this.hiddenField.dom.value = '';
12096         }
12097         this.value = '';
12098         this.setRawValue('');
12099         this.lastSelectionText = '';
12100         this.lastData = false;
12101         
12102         var close = this.closeTriggerEl();
12103         
12104         if(close){
12105             close.hide();
12106         }
12107         
12108     },
12109
12110     /**
12111      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12112      * will be displayed in the field.  If the value does not match the data value of an existing item,
12113      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12114      * Otherwise the field will be blank (although the value will still be set).
12115      * @param {String} value The value to match
12116      */
12117     setValue : function(v){
12118         if(this.multiple){
12119             this.syncValue();
12120             return;
12121         }
12122         
12123         var text = v;
12124         if(this.valueField){
12125             var r = this.findRecord(this.valueField, v);
12126             if(r){
12127                 text = r.data[this.displayField];
12128             }else if(this.valueNotFoundText !== undefined){
12129                 text = this.valueNotFoundText;
12130             }
12131         }
12132         this.lastSelectionText = text;
12133         if(this.hiddenField){
12134             this.hiddenField.dom.value = v;
12135         }
12136         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12137         this.value = v;
12138         
12139         var close = this.closeTriggerEl();
12140         
12141         if(close){
12142             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12143         }
12144     },
12145     /**
12146      * @property {Object} the last set data for the element
12147      */
12148     
12149     lastData : false,
12150     /**
12151      * Sets the value of the field based on a object which is related to the record format for the store.
12152      * @param {Object} value the value to set as. or false on reset?
12153      */
12154     setFromData : function(o){
12155         
12156         if(this.multiple){
12157             this.addItem(o);
12158             return;
12159         }
12160             
12161         var dv = ''; // display value
12162         var vv = ''; // value value..
12163         this.lastData = o;
12164         if (this.displayField) {
12165             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12166         } else {
12167             // this is an error condition!!!
12168             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12169         }
12170         
12171         if(this.valueField){
12172             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12173         }
12174         
12175         var close = this.closeTriggerEl();
12176         
12177         if(close){
12178             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12179         }
12180         
12181         if(this.hiddenField){
12182             this.hiddenField.dom.value = vv;
12183             
12184             this.lastSelectionText = dv;
12185             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12186             this.value = vv;
12187             return;
12188         }
12189         // no hidden field.. - we store the value in 'value', but still display
12190         // display field!!!!
12191         this.lastSelectionText = dv;
12192         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12193         this.value = vv;
12194         
12195         
12196         
12197     },
12198     // private
12199     reset : function(){
12200         // overridden so that last data is reset..
12201         
12202         if(this.multiple){
12203             this.clearItem();
12204             return;
12205         }
12206         
12207         this.setValue(this.originalValue);
12208         this.clearInvalid();
12209         this.lastData = false;
12210         if (this.view) {
12211             this.view.clearSelections();
12212         }
12213     },
12214     // private
12215     findRecord : function(prop, value){
12216         var record;
12217         if(this.store.getCount() > 0){
12218             this.store.each(function(r){
12219                 if(r.data[prop] == value){
12220                     record = r;
12221                     return false;
12222                 }
12223                 return true;
12224             });
12225         }
12226         return record;
12227     },
12228     
12229     getName: function()
12230     {
12231         // returns hidden if it's set..
12232         if (!this.rendered) {return ''};
12233         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12234         
12235     },
12236     // private
12237     onViewMove : function(e, t){
12238         this.inKeyMode = false;
12239     },
12240
12241     // private
12242     onViewOver : function(e, t){
12243         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12244             return;
12245         }
12246         var item = this.view.findItemFromChild(t);
12247         
12248         if(item){
12249             var index = this.view.indexOf(item);
12250             this.select(index, false);
12251         }
12252     },
12253
12254     // private
12255     onViewClick : function(view, doFocus, el, e)
12256     {
12257         var index = this.view.getSelectedIndexes()[0];
12258         
12259         var r = this.store.getAt(index);
12260         
12261         if(this.tickable){
12262             
12263             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12264                 return;
12265             }
12266             
12267             var rm = false;
12268             var _this = this;
12269             
12270             Roo.each(this.tickItems, function(v,k){
12271                 
12272                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12273                     _this.tickItems.splice(k, 1);
12274                     
12275                     if(typeof(e) == 'undefined' && view == false){
12276                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12277                     }
12278                     
12279                     rm = true;
12280                     return;
12281                 }
12282             });
12283             
12284             if(rm){
12285                 return;
12286             }
12287             
12288             this.tickItems.push(r.data);
12289             
12290             if(typeof(e) == 'undefined' && view == false){
12291                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12292             }
12293                     
12294             return;
12295         }
12296         
12297         if(r){
12298             this.onSelect(r, index);
12299         }
12300         if(doFocus !== false && !this.blockFocus){
12301             this.inputEl().focus();
12302         }
12303     },
12304
12305     // private
12306     restrictHeight : function(){
12307         //this.innerList.dom.style.height = '';
12308         //var inner = this.innerList.dom;
12309         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12310         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12311         //this.list.beginUpdate();
12312         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12313         this.list.alignTo(this.inputEl(), this.listAlign);
12314         this.list.alignTo(this.inputEl(), this.listAlign);
12315         //this.list.endUpdate();
12316     },
12317
12318     // private
12319     onEmptyResults : function(){
12320         
12321         if(this.tickable && this.editable){
12322             this.restrictHeight();
12323             return;
12324         }
12325         
12326         this.collapse();
12327     },
12328
12329     /**
12330      * Returns true if the dropdown list is expanded, else false.
12331      */
12332     isExpanded : function(){
12333         return this.list.isVisible();
12334     },
12335
12336     /**
12337      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12338      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12339      * @param {String} value The data value of the item to select
12340      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12341      * selected item if it is not currently in view (defaults to true)
12342      * @return {Boolean} True if the value matched an item in the list, else false
12343      */
12344     selectByValue : function(v, scrollIntoView){
12345         if(v !== undefined && v !== null){
12346             var r = this.findRecord(this.valueField || this.displayField, v);
12347             if(r){
12348                 this.select(this.store.indexOf(r), scrollIntoView);
12349                 return true;
12350             }
12351         }
12352         return false;
12353     },
12354
12355     /**
12356      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12357      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12358      * @param {Number} index The zero-based index of the list item to select
12359      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12360      * selected item if it is not currently in view (defaults to true)
12361      */
12362     select : function(index, scrollIntoView){
12363         this.selectedIndex = index;
12364         this.view.select(index);
12365         if(scrollIntoView !== false){
12366             var el = this.view.getNode(index);
12367             /*
12368              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12369              */
12370             if(el){
12371                 this.list.scrollChildIntoView(el, false);
12372             }
12373         }
12374     },
12375
12376     // private
12377     selectNext : function(){
12378         var ct = this.store.getCount();
12379         if(ct > 0){
12380             if(this.selectedIndex == -1){
12381                 this.select(0);
12382             }else if(this.selectedIndex < ct-1){
12383                 this.select(this.selectedIndex+1);
12384             }
12385         }
12386     },
12387
12388     // private
12389     selectPrev : function(){
12390         var ct = this.store.getCount();
12391         if(ct > 0){
12392             if(this.selectedIndex == -1){
12393                 this.select(0);
12394             }else if(this.selectedIndex != 0){
12395                 this.select(this.selectedIndex-1);
12396             }
12397         }
12398     },
12399
12400     // private
12401     onKeyUp : function(e){
12402         if(this.editable !== false && !e.isSpecialKey()){
12403             this.lastKey = e.getKey();
12404             this.dqTask.delay(this.queryDelay);
12405         }
12406     },
12407
12408     // private
12409     validateBlur : function(){
12410         return !this.list || !this.list.isVisible();   
12411     },
12412
12413     // private
12414     initQuery : function(){
12415         
12416         var v = this.getRawValue();
12417         
12418         if(this.tickable && this.editable){
12419             v = this.tickableInputEl().getValue();
12420         }
12421         
12422         this.doQuery(v);
12423     },
12424
12425     // private
12426     doForce : function(){
12427         if(this.inputEl().dom.value.length > 0){
12428             this.inputEl().dom.value =
12429                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12430              
12431         }
12432     },
12433
12434     /**
12435      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12436      * query allowing the query action to be canceled if needed.
12437      * @param {String} query The SQL query to execute
12438      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12439      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12440      * saved in the current store (defaults to false)
12441      */
12442     doQuery : function(q, forceAll){
12443         
12444         if(q === undefined || q === null){
12445             q = '';
12446         }
12447         var qe = {
12448             query: q,
12449             forceAll: forceAll,
12450             combo: this,
12451             cancel:false
12452         };
12453         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12454             return false;
12455         }
12456         q = qe.query;
12457         
12458         forceAll = qe.forceAll;
12459         if(forceAll === true || (q.length >= this.minChars)){
12460             
12461             this.hasQuery = true;
12462             
12463             if(this.lastQuery != q || this.alwaysQuery){
12464                 this.lastQuery = q;
12465                 if(this.mode == 'local'){
12466                     this.selectedIndex = -1;
12467                     if(forceAll){
12468                         this.store.clearFilter();
12469                     }else{
12470                         
12471                         if(this.specialFilter){
12472                             this.fireEvent('specialfilter', this);
12473                             this.onLoad();
12474                             return;
12475                         }
12476                         
12477                         this.store.filter(this.displayField, q);
12478                     }
12479                     
12480                     this.store.fireEvent("datachanged", this.store);
12481                     
12482                     this.onLoad();
12483                     
12484                     
12485                 }else{
12486                     
12487                     this.store.baseParams[this.queryParam] = q;
12488                     
12489                     var options = {params : this.getParams(q)};
12490                     
12491                     if(this.loadNext){
12492                         options.add = true;
12493                         options.params.start = this.page * this.pageSize;
12494                     }
12495                     
12496                     this.store.load(options);
12497                     
12498                     /*
12499                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12500                      *  we should expand the list on onLoad
12501                      *  so command out it
12502                      */
12503 //                    this.expand();
12504                 }
12505             }else{
12506                 this.selectedIndex = -1;
12507                 this.onLoad();   
12508             }
12509         }
12510         
12511         this.loadNext = false;
12512     },
12513     
12514     // private
12515     getParams : function(q){
12516         var p = {};
12517         //p[this.queryParam] = q;
12518         
12519         if(this.pageSize){
12520             p.start = 0;
12521             p.limit = this.pageSize;
12522         }
12523         return p;
12524     },
12525
12526     /**
12527      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12528      */
12529     collapse : function(){
12530         if(!this.isExpanded()){
12531             return;
12532         }
12533         
12534         this.list.hide();
12535         
12536         if(this.tickable){
12537             this.hasFocus = false;
12538             this.okBtn.hide();
12539             this.cancelBtn.hide();
12540             this.trigger.show();
12541             
12542             if(this.editable){
12543                 this.tickableInputEl().dom.value = '';
12544                 this.tickableInputEl().blur();
12545             }
12546             
12547         }
12548         
12549         Roo.get(document).un('mousedown', this.collapseIf, this);
12550         Roo.get(document).un('mousewheel', this.collapseIf, this);
12551         if (!this.editable) {
12552             Roo.get(document).un('keydown', this.listKeyPress, this);
12553         }
12554         this.fireEvent('collapse', this);
12555     },
12556
12557     // private
12558     collapseIf : function(e){
12559         var in_combo  = e.within(this.el);
12560         var in_list =  e.within(this.list);
12561         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12562         
12563         if (in_combo || in_list || is_list) {
12564             //e.stopPropagation();
12565             return;
12566         }
12567         
12568         if(this.tickable){
12569             this.onTickableFooterButtonClick(e, false, false);
12570         }
12571
12572         this.collapse();
12573         
12574     },
12575
12576     /**
12577      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12578      */
12579     expand : function(){
12580        
12581         if(this.isExpanded() || !this.hasFocus){
12582             return;
12583         }
12584         
12585         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12586         this.list.setWidth(lw);
12587         
12588         
12589          Roo.log('expand');
12590         
12591         this.list.show();
12592         
12593         this.restrictHeight();
12594         
12595         if(this.tickable){
12596             
12597             this.tickItems = Roo.apply([], this.item);
12598             
12599             this.okBtn.show();
12600             this.cancelBtn.show();
12601             this.trigger.hide();
12602             
12603             if(this.editable){
12604                 this.tickableInputEl().focus();
12605             }
12606             
12607         }
12608         
12609         Roo.get(document).on('mousedown', this.collapseIf, this);
12610         Roo.get(document).on('mousewheel', this.collapseIf, this);
12611         if (!this.editable) {
12612             Roo.get(document).on('keydown', this.listKeyPress, this);
12613         }
12614         
12615         this.fireEvent('expand', this);
12616     },
12617
12618     // private
12619     // Implements the default empty TriggerField.onTriggerClick function
12620     onTriggerClick : function(e)
12621     {
12622         Roo.log('trigger click');
12623         
12624         if(this.disabled || !this.triggerList){
12625             return;
12626         }
12627         
12628         this.page = 0;
12629         this.loadNext = false;
12630         
12631         if(this.isExpanded()){
12632             this.collapse();
12633             if (!this.blockFocus) {
12634                 this.inputEl().focus();
12635             }
12636             
12637         }else {
12638             this.hasFocus = true;
12639             if(this.triggerAction == 'all') {
12640                 this.doQuery(this.allQuery, true);
12641             } else {
12642                 this.doQuery(this.getRawValue());
12643             }
12644             if (!this.blockFocus) {
12645                 this.inputEl().focus();
12646             }
12647         }
12648     },
12649     
12650     onTickableTriggerClick : function(e)
12651     {
12652         if(this.disabled){
12653             return;
12654         }
12655         
12656         this.page = 0;
12657         this.loadNext = false;
12658         this.hasFocus = true;
12659         
12660         if(this.triggerAction == 'all') {
12661             this.doQuery(this.allQuery, true);
12662         } else {
12663             this.doQuery(this.getRawValue());
12664         }
12665     },
12666     
12667     onSearchFieldClick : function(e)
12668     {
12669         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12670             this.onTickableFooterButtonClick(e, false, false);
12671             return;
12672         }
12673         
12674         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12675             return;
12676         }
12677         
12678         this.page = 0;
12679         this.loadNext = false;
12680         this.hasFocus = true;
12681         
12682         if(this.triggerAction == 'all') {
12683             this.doQuery(this.allQuery, true);
12684         } else {
12685             this.doQuery(this.getRawValue());
12686         }
12687     },
12688     
12689     listKeyPress : function(e)
12690     {
12691         //Roo.log('listkeypress');
12692         // scroll to first matching element based on key pres..
12693         if (e.isSpecialKey()) {
12694             return false;
12695         }
12696         var k = String.fromCharCode(e.getKey()).toUpperCase();
12697         //Roo.log(k);
12698         var match  = false;
12699         var csel = this.view.getSelectedNodes();
12700         var cselitem = false;
12701         if (csel.length) {
12702             var ix = this.view.indexOf(csel[0]);
12703             cselitem  = this.store.getAt(ix);
12704             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12705                 cselitem = false;
12706             }
12707             
12708         }
12709         
12710         this.store.each(function(v) { 
12711             if (cselitem) {
12712                 // start at existing selection.
12713                 if (cselitem.id == v.id) {
12714                     cselitem = false;
12715                 }
12716                 return true;
12717             }
12718                 
12719             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12720                 match = this.store.indexOf(v);
12721                 return false;
12722             }
12723             return true;
12724         }, this);
12725         
12726         if (match === false) {
12727             return true; // no more action?
12728         }
12729         // scroll to?
12730         this.view.select(match);
12731         var sn = Roo.get(this.view.getSelectedNodes()[0])
12732         sn.scrollIntoView(sn.dom.parentNode, false);
12733     },
12734     
12735     onViewScroll : function(e, t){
12736         
12737         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){
12738             return;
12739         }
12740         
12741         this.hasQuery = true;
12742         
12743         this.loading = this.list.select('.loading', true).first();
12744         
12745         if(this.loading === null){
12746             this.list.createChild({
12747                 tag: 'div',
12748                 cls: 'loading select2-more-results select2-active',
12749                 html: 'Loading more results...'
12750             })
12751             
12752             this.loading = this.list.select('.loading', true).first();
12753             
12754             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12755             
12756             this.loading.hide();
12757         }
12758         
12759         this.loading.show();
12760         
12761         var _combo = this;
12762         
12763         this.page++;
12764         this.loadNext = true;
12765         
12766         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12767         
12768         return;
12769     },
12770     
12771     addItem : function(o)
12772     {   
12773         var dv = ''; // display value
12774         
12775         if (this.displayField) {
12776             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12777         } else {
12778             // this is an error condition!!!
12779             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12780         }
12781         
12782         if(!dv.length){
12783             return;
12784         }
12785         
12786         var choice = this.choices.createChild({
12787             tag: 'li',
12788             cls: 'select2-search-choice',
12789             cn: [
12790                 {
12791                     tag: 'div',
12792                     html: dv
12793                 },
12794                 {
12795                     tag: 'a',
12796                     href: '#',
12797                     cls: 'select2-search-choice-close',
12798                     tabindex: '-1'
12799                 }
12800             ]
12801             
12802         }, this.searchField);
12803         
12804         var close = choice.select('a.select2-search-choice-close', true).first()
12805         
12806         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12807         
12808         this.item.push(o);
12809         
12810         this.lastData = o;
12811         
12812         this.syncValue();
12813         
12814         this.inputEl().dom.value = '';
12815         
12816         this.validate();
12817     },
12818     
12819     onRemoveItem : function(e, _self, o)
12820     {
12821         e.preventDefault();
12822         
12823         this.lastItem = Roo.apply([], this.item);
12824         
12825         var index = this.item.indexOf(o.data) * 1;
12826         
12827         if( index < 0){
12828             Roo.log('not this item?!');
12829             return;
12830         }
12831         
12832         this.item.splice(index, 1);
12833         o.item.remove();
12834         
12835         this.syncValue();
12836         
12837         this.fireEvent('remove', this, e);
12838         
12839         this.validate();
12840         
12841     },
12842     
12843     syncValue : function()
12844     {
12845         if(!this.item.length){
12846             this.clearValue();
12847             return;
12848         }
12849             
12850         var value = [];
12851         var _this = this;
12852         Roo.each(this.item, function(i){
12853             if(_this.valueField){
12854                 value.push(i[_this.valueField]);
12855                 return;
12856             }
12857
12858             value.push(i);
12859         });
12860
12861         this.value = value.join(',');
12862
12863         if(this.hiddenField){
12864             this.hiddenField.dom.value = this.value;
12865         }
12866         
12867         this.store.fireEvent("datachanged", this.store);
12868     },
12869     
12870     clearItem : function()
12871     {
12872         if(!this.multiple){
12873             return;
12874         }
12875         
12876         this.item = [];
12877         
12878         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12879            c.remove();
12880         });
12881         
12882         this.syncValue();
12883         
12884         this.validate();
12885     },
12886     
12887     inputEl: function ()
12888     {
12889         if(Roo.isTouch && this.mobileTouchView){
12890             return this.el.select('input.form-control',true).first();
12891         }
12892         
12893         if(this.tickable){
12894             return this.searchField;
12895         }
12896         
12897         return this.el.select('input.form-control',true).first();
12898     },
12899     
12900     
12901     onTickableFooterButtonClick : function(e, btn, el)
12902     {
12903         e.preventDefault();
12904         
12905         this.lastItem = Roo.apply([], this.item);
12906         
12907         if(btn && btn.name == 'cancel'){
12908             this.tickItems = Roo.apply([], this.item);
12909             this.collapse();
12910             return;
12911         }
12912         
12913         this.clearItem();
12914         
12915         var _this = this;
12916         
12917         Roo.each(this.tickItems, function(o){
12918             _this.addItem(o);
12919         });
12920         
12921         this.collapse();
12922         
12923     },
12924     
12925     validate : function()
12926     {
12927         var v = this.getRawValue();
12928         
12929         if(this.multiple){
12930             v = this.getValue();
12931         }
12932         
12933         if(this.disabled || this.allowBlank || v.length){
12934             this.markValid();
12935             return true;
12936         }
12937         
12938         this.markInvalid();
12939         return false;
12940     },
12941     
12942     tickableInputEl : function()
12943     {
12944         if(!this.tickable || !this.editable){
12945             return this.inputEl();
12946         }
12947         
12948         return this.inputEl().select('.select2-search-field-input', true).first();
12949     },
12950     
12951     
12952     getAutoCreateTouchView : function()
12953     {
12954         var id = Roo.id();
12955         
12956         var cfg = {
12957             cls: 'form-group' //input-group
12958         };
12959         
12960         var input =  {
12961             tag: 'input',
12962             id : id,
12963             type : this.inputType,
12964             cls : 'form-control x-combo-noedit',
12965             autocomplete: 'new-password',
12966             placeholder : this.placeholder || '',
12967             readonly : true
12968         };
12969         
12970         if (this.name) {
12971             input.name = this.name;
12972         }
12973         
12974         if (this.size) {
12975             input.cls += ' input-' + this.size;
12976         }
12977         
12978         if (this.disabled) {
12979             input.disabled = true;
12980         }
12981         
12982         var inputblock = {
12983             cls : '',
12984             cn : [
12985                 input
12986             ]
12987         };
12988         
12989         if(this.before){
12990             inputblock.cls += ' input-group';
12991             
12992             inputblock.cn.unshift({
12993                 tag :'span',
12994                 cls : 'input-group-addon',
12995                 html : this.before
12996             });
12997         }
12998         
12999         if(this.removable && !this.multiple){
13000             inputblock.cls += ' roo-removable';
13001             
13002             inputblock.cn.push({
13003                 tag: 'button',
13004                 html : 'x',
13005                 cls : 'roo-combo-removable-btn close'
13006             });
13007         }
13008
13009         if(this.hasFeedback && !this.allowBlank){
13010             
13011             inputblock.cls += ' has-feedback';
13012             
13013             inputblock.cn.push({
13014                 tag: 'span',
13015                 cls: 'glyphicon form-control-feedback'
13016             });
13017             
13018         }
13019         
13020         if (this.after) {
13021             
13022             inputblock.cls += (this.before) ? '' : ' input-group';
13023             
13024             inputblock.cn.push({
13025                 tag :'span',
13026                 cls : 'input-group-addon',
13027                 html : this.after
13028             });
13029         }
13030
13031         var box = {
13032             tag: 'div',
13033             cn: [
13034                 {
13035                     tag: 'input',
13036                     type : 'hidden',
13037                     cls: 'form-hidden-field'
13038                 },
13039                 inputblock
13040             ]
13041             
13042         };
13043         
13044         if(this.multiple){
13045             box = {
13046                 tag: 'div',
13047                 cn: [
13048                     {
13049                         tag: 'input',
13050                         type : 'hidden',
13051                         cls: 'form-hidden-field'
13052                     },
13053                     {
13054                         tag: 'ul',
13055                         cls: 'select2-choices',
13056                         cn:[
13057                             {
13058                                 tag: 'li',
13059                                 cls: 'select2-search-field',
13060                                 cn: [
13061
13062                                     inputblock
13063                                 ]
13064                             }
13065                         ]
13066                     }
13067                 ]
13068             }
13069         };
13070         
13071         var combobox = {
13072             cls: 'select2-container input-group',
13073             cn: [
13074                 box
13075             ]
13076         };
13077         
13078         if(this.multiple){
13079             combobox.cls += ' select2-container-multi';
13080         }
13081         
13082         var align = this.labelAlign || this.parentLabelAlign();
13083         
13084         cfg.cn = combobox;
13085         
13086         if(this.fieldLabel.length){
13087             
13088             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13089             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13090             
13091             cfg.cn = [
13092                 {
13093                     tag: 'label',
13094                     cls : 'control-label ' + lw,
13095                     html : this.fieldLabel
13096
13097                 },
13098                 {
13099                     cls : cw, 
13100                     cn: [
13101                         combobox
13102                     ]
13103                 }
13104             ];
13105         }
13106         
13107         var settings = this;
13108         
13109         ['xs','sm','md','lg'].map(function(size){
13110             if (settings[size]) {
13111                 cfg.cls += ' col-' + size + '-' + settings[size];
13112             }
13113         });
13114         
13115         return cfg;
13116     },
13117     
13118     initTouchView : function()
13119     {
13120         this.renderTouchView();
13121         
13122         this.touchViewEl.on('scroll', function(){
13123             this.el.dom.scrollTop = 0;
13124         }, this);
13125         
13126         this.inputEl().on("click", this.showTouchView, this);
13127         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13128         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13129         
13130         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13131         
13132         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13133         this.store.on('load', this.onTouchViewLoad, this);
13134         this.store.on('loadexception', this.onTouchViewLoadException, this);
13135         
13136         if(this.hiddenName){
13137             
13138             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13139             
13140             this.hiddenField.dom.value =
13141                 this.hiddenValue !== undefined ? this.hiddenValue :
13142                 this.value !== undefined ? this.value : '';
13143         
13144             this.el.dom.removeAttribute('name');
13145             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13146         }
13147         
13148         if(this.multiple){
13149             this.choices = this.el.select('ul.select2-choices', true).first();
13150             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13151         }
13152         
13153         if(this.removable && !this.multiple){
13154             var close = this.closeTriggerEl();
13155             if(close){
13156                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13157                 close.on('click', this.removeBtnClick, this, close);
13158             }
13159         }
13160         
13161         return;
13162         
13163         
13164     },
13165     
13166     renderTouchView : function()
13167     {
13168         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13169         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13170         
13171         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13172         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13173         
13174         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13175         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13176         this.touchViewBodyEl.setStyle('overflow', 'auto');
13177         
13178         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13179         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13180         
13181         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13182         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13183         
13184     },
13185     
13186     showTouchView : function()
13187     {
13188         this.touchViewHeaderEl.hide();
13189
13190         if(this.fieldLabel.length){
13191             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13192             this.touchViewHeaderEl.show();
13193         }
13194
13195         this.touchViewEl.show();
13196
13197         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13198         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13199
13200         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13201
13202         if(this.fieldLabel.length){
13203             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13204         }
13205         
13206         this.touchViewBodyEl.setHeight(bodyHeight);
13207
13208         if(this.animate){
13209             var _this = this;
13210             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13211         }else{
13212             this.touchViewEl.addClass('in');
13213         }
13214
13215         this.doTouchViewQuery();
13216         
13217     },
13218     
13219     hideTouchView : function()
13220     {
13221         this.touchViewEl.removeClass('in');
13222
13223         if(this.animate){
13224             var _this = this;
13225             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13226         }else{
13227             this.touchViewEl.setStyle('display', 'none');
13228         }
13229         
13230     },
13231     
13232     setTouchViewValue : function()
13233     {
13234         if(this.multiple){
13235             this.clearItem();
13236         
13237             var _this = this;
13238
13239             Roo.each(this.tickItems, function(o){
13240                 this.addItem(o);
13241             }, this);
13242         }
13243         
13244         this.hideTouchView();
13245     },
13246     
13247     doTouchViewQuery : function()
13248     {
13249         var qe = {
13250             query: '',
13251             forceAll: true,
13252             combo: this,
13253             cancel:false
13254         };
13255         
13256         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13257             return false;
13258         }
13259         
13260         if(!this.alwaysQuery || this.mode == 'local'){
13261             this.onTouchViewLoad();
13262             return;
13263         }
13264         
13265         this.store.load();
13266     },
13267     
13268     onTouchViewBeforeLoad : function(combo,opts)
13269     {
13270         return;
13271     },
13272
13273     // private
13274     onTouchViewLoad : function()
13275     {
13276         if(this.store.getCount() < 1){
13277             this.onTouchViewEmptyResults();
13278             return;
13279         }
13280         
13281         this.clearTouchView();
13282         
13283         var rawValue = this.getRawValue();
13284         
13285         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13286         
13287         this.tickItems = [];
13288         
13289         this.store.data.each(function(d, rowIndex){
13290             var row = this.touchViewListGroup.createChild(template);
13291             
13292             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13293                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13294             }
13295             
13296             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13297                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13298             }
13299             
13300             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13301                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13302                 this.tickItems.push(d.data);
13303             }
13304             
13305             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13306             
13307         }, this);
13308         
13309         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13310         
13311         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13312
13313         if(this.fieldLabel.length){
13314             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13315         }
13316
13317         var listHeight = this.touchViewListGroup.getHeight();
13318         
13319         if(firstChecked && listHeight > bodyHeight){
13320             (function() { firstChecked.findParent('li').scrollIntoView(this.touchViewListGroup.dom); }).defer(500);
13321         }
13322         
13323     },
13324     
13325     onTouchViewLoadException : function()
13326     {
13327         this.hideTouchView();
13328     },
13329     
13330     onTouchViewEmptyResults : function()
13331     {
13332         this.clearTouchView();
13333         
13334         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13335         
13336         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13337         
13338     },
13339     
13340     clearTouchView : function()
13341     {
13342         this.touchViewListGroup.dom.innerHTML = '';
13343     },
13344     
13345     onTouchViewClick : function(e, el, o)
13346     {
13347         e.preventDefault();
13348         
13349         var row = o.row;
13350         var rowIndex = o.rowIndex;
13351         
13352         var r = this.store.getAt(rowIndex);
13353         
13354         if(!this.multiple){
13355             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13356                 c.dom.removeAttribute('checked');
13357             }, this);
13358             
13359             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13360         
13361             this.setFromData(r.data);
13362             
13363             var close = this.closeTriggerEl();
13364         
13365             if(close){
13366                 close.show();
13367             }
13368
13369             this.hideTouchView();
13370             
13371             return;
13372         }
13373         
13374         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13375             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13376             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13377             return;
13378         }
13379         
13380         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13381         this.addItem(r.data);
13382         this.tickItems.push(r.data);
13383         
13384         
13385         
13386     }
13387     
13388
13389     /** 
13390     * @cfg {Boolean} grow 
13391     * @hide 
13392     */
13393     /** 
13394     * @cfg {Number} growMin 
13395     * @hide 
13396     */
13397     /** 
13398     * @cfg {Number} growMax 
13399     * @hide 
13400     */
13401     /**
13402      * @hide
13403      * @method autoSize
13404      */
13405 });
13406
13407 Roo.apply(Roo.bootstrap.ComboBox,  {
13408     
13409     header : {
13410         tag: 'div',
13411         cls: 'modal-header',
13412         cn: [
13413             {
13414                 tag: 'h4',
13415                 cls: 'modal-title'
13416             }
13417         ]
13418     },
13419     
13420     body : {
13421         tag: 'div',
13422         cls: 'modal-body',
13423         cn: [
13424             {
13425                 tag: 'ul',
13426                 cls: 'list-group'
13427             }
13428         ]
13429     },
13430     
13431     listItemRadio : {
13432         tag: 'li',
13433         cls: 'list-group-item',
13434         cn: [
13435             {
13436                 tag: 'span',
13437                 cls: 'roo-combobox-list-group-item-value'
13438             },
13439             {
13440                 tag: 'div',
13441                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13442                 cn: [
13443                     {
13444                         tag: 'input',
13445                         type: 'radio'
13446                     },
13447                     {
13448                         tag: 'label'
13449                     }
13450                 ]
13451             }
13452         ]
13453     },
13454     
13455     listItemCheckbox : {
13456         tag: 'li',
13457         cls: 'list-group-item',
13458         cn: [
13459             {
13460                 tag: 'span',
13461                 cls: 'roo-combobox-list-group-item-value'
13462             },
13463             {
13464                 tag: 'div',
13465                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13466                 cn: [
13467                     {
13468                         tag: 'input',
13469                         type: 'checkbox'
13470                     },
13471                     {
13472                         tag: 'label'
13473                     }
13474                 ]
13475             }
13476         ]
13477     },
13478     
13479     emptyResult : {
13480         tag: 'div',
13481         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13482     },
13483     
13484     footer : {
13485         tag: 'div',
13486         cls: 'modal-footer',
13487         cn: [
13488             {
13489                 tag: 'div',
13490                 cls: 'row',
13491                 cn: [
13492                     {
13493                         tag: 'div',
13494                         cls: 'col-xs-6 text-left',
13495                         cn: {
13496                             tag: 'button',
13497                             cls: 'btn btn-danger roo-touch-view-cancel',
13498                             html: 'Cancel'
13499                         }
13500                     },
13501                     {
13502                         tag: 'div',
13503                         cls: 'col-xs-6 text-right',
13504                         cn: {
13505                             tag: 'button',
13506                             cls: 'btn btn-success roo-touch-view-ok',
13507                             html: 'OK'
13508                         }
13509                     }
13510                 ]
13511             }
13512         ]
13513         
13514     }
13515 });
13516
13517 Roo.apply(Roo.bootstrap.ComboBox,  {
13518     
13519     touchViewTemplate : {
13520         tag: 'div',
13521         cls: 'modal fade roo-combobox-touch-view',
13522         cn: [
13523             {
13524                 tag: 'div',
13525                 cls: 'modal-dialog',
13526                 cn: [
13527                     {
13528                         tag: 'div',
13529                         cls: 'modal-content',
13530                         cn: [
13531                             Roo.bootstrap.ComboBox.header,
13532                             Roo.bootstrap.ComboBox.body,
13533                             Roo.bootstrap.ComboBox.footer
13534                         ]
13535                     }
13536                 ]
13537             }
13538         ]
13539     }
13540 });/*
13541  * Based on:
13542  * Ext JS Library 1.1.1
13543  * Copyright(c) 2006-2007, Ext JS, LLC.
13544  *
13545  * Originally Released Under LGPL - original licence link has changed is not relivant.
13546  *
13547  * Fork - LGPL
13548  * <script type="text/javascript">
13549  */
13550
13551 /**
13552  * @class Roo.View
13553  * @extends Roo.util.Observable
13554  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13555  * This class also supports single and multi selection modes. <br>
13556  * Create a data model bound view:
13557  <pre><code>
13558  var store = new Roo.data.Store(...);
13559
13560  var view = new Roo.View({
13561     el : "my-element",
13562     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13563  
13564     singleSelect: true,
13565     selectedClass: "ydataview-selected",
13566     store: store
13567  });
13568
13569  // listen for node click?
13570  view.on("click", function(vw, index, node, e){
13571  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13572  });
13573
13574  // load XML data
13575  dataModel.load("foobar.xml");
13576  </code></pre>
13577  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13578  * <br><br>
13579  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13580  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13581  * 
13582  * Note: old style constructor is still suported (container, template, config)
13583  * 
13584  * @constructor
13585  * Create a new View
13586  * @param {Object} config The config object
13587  * 
13588  */
13589 Roo.View = function(config, depreciated_tpl, depreciated_config){
13590     
13591     this.parent = false;
13592     
13593     if (typeof(depreciated_tpl) == 'undefined') {
13594         // new way.. - universal constructor.
13595         Roo.apply(this, config);
13596         this.el  = Roo.get(this.el);
13597     } else {
13598         // old format..
13599         this.el  = Roo.get(config);
13600         this.tpl = depreciated_tpl;
13601         Roo.apply(this, depreciated_config);
13602     }
13603     this.wrapEl  = this.el.wrap().wrap();
13604     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13605     
13606     
13607     if(typeof(this.tpl) == "string"){
13608         this.tpl = new Roo.Template(this.tpl);
13609     } else {
13610         // support xtype ctors..
13611         this.tpl = new Roo.factory(this.tpl, Roo);
13612     }
13613     
13614     
13615     this.tpl.compile();
13616     
13617     /** @private */
13618     this.addEvents({
13619         /**
13620          * @event beforeclick
13621          * Fires before a click is processed. Returns false to cancel the default action.
13622          * @param {Roo.View} this
13623          * @param {Number} index The index of the target node
13624          * @param {HTMLElement} node The target node
13625          * @param {Roo.EventObject} e The raw event object
13626          */
13627             "beforeclick" : true,
13628         /**
13629          * @event click
13630          * Fires when a template node is clicked.
13631          * @param {Roo.View} this
13632          * @param {Number} index The index of the target node
13633          * @param {HTMLElement} node The target node
13634          * @param {Roo.EventObject} e The raw event object
13635          */
13636             "click" : true,
13637         /**
13638          * @event dblclick
13639          * Fires when a template node is double clicked.
13640          * @param {Roo.View} this
13641          * @param {Number} index The index of the target node
13642          * @param {HTMLElement} node The target node
13643          * @param {Roo.EventObject} e The raw event object
13644          */
13645             "dblclick" : true,
13646         /**
13647          * @event contextmenu
13648          * Fires when a template node is right clicked.
13649          * @param {Roo.View} this
13650          * @param {Number} index The index of the target node
13651          * @param {HTMLElement} node The target node
13652          * @param {Roo.EventObject} e The raw event object
13653          */
13654             "contextmenu" : true,
13655         /**
13656          * @event selectionchange
13657          * Fires when the selected nodes change.
13658          * @param {Roo.View} this
13659          * @param {Array} selections Array of the selected nodes
13660          */
13661             "selectionchange" : true,
13662     
13663         /**
13664          * @event beforeselect
13665          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13666          * @param {Roo.View} this
13667          * @param {HTMLElement} node The node to be selected
13668          * @param {Array} selections Array of currently selected nodes
13669          */
13670             "beforeselect" : true,
13671         /**
13672          * @event preparedata
13673          * Fires on every row to render, to allow you to change the data.
13674          * @param {Roo.View} this
13675          * @param {Object} data to be rendered (change this)
13676          */
13677           "preparedata" : true
13678           
13679           
13680         });
13681
13682
13683
13684     this.el.on({
13685         "click": this.onClick,
13686         "dblclick": this.onDblClick,
13687         "contextmenu": this.onContextMenu,
13688         scope:this
13689     });
13690
13691     this.selections = [];
13692     this.nodes = [];
13693     this.cmp = new Roo.CompositeElementLite([]);
13694     if(this.store){
13695         this.store = Roo.factory(this.store, Roo.data);
13696         this.setStore(this.store, true);
13697     }
13698     
13699     if ( this.footer && this.footer.xtype) {
13700            
13701          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13702         
13703         this.footer.dataSource = this.store
13704         this.footer.container = fctr;
13705         this.footer = Roo.factory(this.footer, Roo);
13706         fctr.insertFirst(this.el);
13707         
13708         // this is a bit insane - as the paging toolbar seems to detach the el..
13709 //        dom.parentNode.parentNode.parentNode
13710          // they get detached?
13711     }
13712     
13713     
13714     Roo.View.superclass.constructor.call(this);
13715     
13716     
13717 };
13718
13719 Roo.extend(Roo.View, Roo.util.Observable, {
13720     
13721      /**
13722      * @cfg {Roo.data.Store} store Data store to load data from.
13723      */
13724     store : false,
13725     
13726     /**
13727      * @cfg {String|Roo.Element} el The container element.
13728      */
13729     el : '',
13730     
13731     /**
13732      * @cfg {String|Roo.Template} tpl The template used by this View 
13733      */
13734     tpl : false,
13735     /**
13736      * @cfg {String} dataName the named area of the template to use as the data area
13737      *                          Works with domtemplates roo-name="name"
13738      */
13739     dataName: false,
13740     /**
13741      * @cfg {String} selectedClass The css class to add to selected nodes
13742      */
13743     selectedClass : "x-view-selected",
13744      /**
13745      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13746      */
13747     emptyText : "",
13748     
13749     /**
13750      * @cfg {String} text to display on mask (default Loading)
13751      */
13752     mask : false,
13753     /**
13754      * @cfg {Boolean} multiSelect Allow multiple selection
13755      */
13756     multiSelect : false,
13757     /**
13758      * @cfg {Boolean} singleSelect Allow single selection
13759      */
13760     singleSelect:  false,
13761     
13762     /**
13763      * @cfg {Boolean} toggleSelect - selecting 
13764      */
13765     toggleSelect : false,
13766     
13767     /**
13768      * @cfg {Boolean} tickable - selecting 
13769      */
13770     tickable : false,
13771     
13772     /**
13773      * Returns the element this view is bound to.
13774      * @return {Roo.Element}
13775      */
13776     getEl : function(){
13777         return this.wrapEl;
13778     },
13779     
13780     
13781
13782     /**
13783      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13784      */
13785     refresh : function(){
13786         //Roo.log('refresh');
13787         var t = this.tpl;
13788         
13789         // if we are using something like 'domtemplate', then
13790         // the what gets used is:
13791         // t.applySubtemplate(NAME, data, wrapping data..)
13792         // the outer template then get' applied with
13793         //     the store 'extra data'
13794         // and the body get's added to the
13795         //      roo-name="data" node?
13796         //      <span class='roo-tpl-{name}'></span> ?????
13797         
13798         
13799         
13800         this.clearSelections();
13801         this.el.update("");
13802         var html = [];
13803         var records = this.store.getRange();
13804         if(records.length < 1) {
13805             
13806             // is this valid??  = should it render a template??
13807             
13808             this.el.update(this.emptyText);
13809             return;
13810         }
13811         var el = this.el;
13812         if (this.dataName) {
13813             this.el.update(t.apply(this.store.meta)); //????
13814             el = this.el.child('.roo-tpl-' + this.dataName);
13815         }
13816         
13817         for(var i = 0, len = records.length; i < len; i++){
13818             var data = this.prepareData(records[i].data, i, records[i]);
13819             this.fireEvent("preparedata", this, data, i, records[i]);
13820             
13821             var d = Roo.apply({}, data);
13822             
13823             if(this.tickable){
13824                 Roo.apply(d, {'roo-id' : Roo.id()});
13825                 
13826                 var _this = this;
13827             
13828                 Roo.each(this.parent.item, function(item){
13829                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13830                         return;
13831                     }
13832                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13833                 });
13834             }
13835             
13836             html[html.length] = Roo.util.Format.trim(
13837                 this.dataName ?
13838                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13839                     t.apply(d)
13840             );
13841         }
13842         
13843         
13844         
13845         el.update(html.join(""));
13846         this.nodes = el.dom.childNodes;
13847         this.updateIndexes(0);
13848     },
13849     
13850
13851     /**
13852      * Function to override to reformat the data that is sent to
13853      * the template for each node.
13854      * DEPRICATED - use the preparedata event handler.
13855      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13856      * a JSON object for an UpdateManager bound view).
13857      */
13858     prepareData : function(data, index, record)
13859     {
13860         this.fireEvent("preparedata", this, data, index, record);
13861         return data;
13862     },
13863
13864     onUpdate : function(ds, record){
13865         // Roo.log('on update');   
13866         this.clearSelections();
13867         var index = this.store.indexOf(record);
13868         var n = this.nodes[index];
13869         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13870         n.parentNode.removeChild(n);
13871         this.updateIndexes(index, index);
13872     },
13873
13874     
13875     
13876 // --------- FIXME     
13877     onAdd : function(ds, records, index)
13878     {
13879         //Roo.log(['on Add', ds, records, index] );        
13880         this.clearSelections();
13881         if(this.nodes.length == 0){
13882             this.refresh();
13883             return;
13884         }
13885         var n = this.nodes[index];
13886         for(var i = 0, len = records.length; i < len; i++){
13887             var d = this.prepareData(records[i].data, i, records[i]);
13888             if(n){
13889                 this.tpl.insertBefore(n, d);
13890             }else{
13891                 
13892                 this.tpl.append(this.el, d);
13893             }
13894         }
13895         this.updateIndexes(index);
13896     },
13897
13898     onRemove : function(ds, record, index){
13899        // Roo.log('onRemove');
13900         this.clearSelections();
13901         var el = this.dataName  ?
13902             this.el.child('.roo-tpl-' + this.dataName) :
13903             this.el; 
13904         
13905         el.dom.removeChild(this.nodes[index]);
13906         this.updateIndexes(index);
13907     },
13908
13909     /**
13910      * Refresh an individual node.
13911      * @param {Number} index
13912      */
13913     refreshNode : function(index){
13914         this.onUpdate(this.store, this.store.getAt(index));
13915     },
13916
13917     updateIndexes : function(startIndex, endIndex){
13918         var ns = this.nodes;
13919         startIndex = startIndex || 0;
13920         endIndex = endIndex || ns.length - 1;
13921         for(var i = startIndex; i <= endIndex; i++){
13922             ns[i].nodeIndex = i;
13923         }
13924     },
13925
13926     /**
13927      * Changes the data store this view uses and refresh the view.
13928      * @param {Store} store
13929      */
13930     setStore : function(store, initial){
13931         if(!initial && this.store){
13932             this.store.un("datachanged", this.refresh);
13933             this.store.un("add", this.onAdd);
13934             this.store.un("remove", this.onRemove);
13935             this.store.un("update", this.onUpdate);
13936             this.store.un("clear", this.refresh);
13937             this.store.un("beforeload", this.onBeforeLoad);
13938             this.store.un("load", this.onLoad);
13939             this.store.un("loadexception", this.onLoad);
13940         }
13941         if(store){
13942           
13943             store.on("datachanged", this.refresh, this);
13944             store.on("add", this.onAdd, this);
13945             store.on("remove", this.onRemove, this);
13946             store.on("update", this.onUpdate, this);
13947             store.on("clear", this.refresh, this);
13948             store.on("beforeload", this.onBeforeLoad, this);
13949             store.on("load", this.onLoad, this);
13950             store.on("loadexception", this.onLoad, this);
13951         }
13952         
13953         if(store){
13954             this.refresh();
13955         }
13956     },
13957     /**
13958      * onbeforeLoad - masks the loading area.
13959      *
13960      */
13961     onBeforeLoad : function(store,opts)
13962     {
13963          //Roo.log('onBeforeLoad');   
13964         if (!opts.add) {
13965             this.el.update("");
13966         }
13967         this.el.mask(this.mask ? this.mask : "Loading" ); 
13968     },
13969     onLoad : function ()
13970     {
13971         this.el.unmask();
13972     },
13973     
13974
13975     /**
13976      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13977      * @param {HTMLElement} node
13978      * @return {HTMLElement} The template node
13979      */
13980     findItemFromChild : function(node){
13981         var el = this.dataName  ?
13982             this.el.child('.roo-tpl-' + this.dataName,true) :
13983             this.el.dom; 
13984         
13985         if(!node || node.parentNode == el){
13986                     return node;
13987             }
13988             var p = node.parentNode;
13989             while(p && p != el){
13990             if(p.parentNode == el){
13991                 return p;
13992             }
13993             p = p.parentNode;
13994         }
13995             return null;
13996     },
13997
13998     /** @ignore */
13999     onClick : function(e){
14000         var item = this.findItemFromChild(e.getTarget());
14001         if(item){
14002             var index = this.indexOf(item);
14003             if(this.onItemClick(item, index, e) !== false){
14004                 this.fireEvent("click", this, index, item, e);
14005             }
14006         }else{
14007             this.clearSelections();
14008         }
14009     },
14010
14011     /** @ignore */
14012     onContextMenu : function(e){
14013         var item = this.findItemFromChild(e.getTarget());
14014         if(item){
14015             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14016         }
14017     },
14018
14019     /** @ignore */
14020     onDblClick : function(e){
14021         var item = this.findItemFromChild(e.getTarget());
14022         if(item){
14023             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14024         }
14025     },
14026
14027     onItemClick : function(item, index, e)
14028     {
14029         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14030             return false;
14031         }
14032         if (this.toggleSelect) {
14033             var m = this.isSelected(item) ? 'unselect' : 'select';
14034             //Roo.log(m);
14035             var _t = this;
14036             _t[m](item, true, false);
14037             return true;
14038         }
14039         if(this.multiSelect || this.singleSelect){
14040             if(this.multiSelect && e.shiftKey && this.lastSelection){
14041                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14042             }else{
14043                 this.select(item, this.multiSelect && e.ctrlKey);
14044                 this.lastSelection = item;
14045             }
14046             
14047             if(!this.tickable){
14048                 e.preventDefault();
14049             }
14050             
14051         }
14052         return true;
14053     },
14054
14055     /**
14056      * Get the number of selected nodes.
14057      * @return {Number}
14058      */
14059     getSelectionCount : function(){
14060         return this.selections.length;
14061     },
14062
14063     /**
14064      * Get the currently selected nodes.
14065      * @return {Array} An array of HTMLElements
14066      */
14067     getSelectedNodes : function(){
14068         return this.selections;
14069     },
14070
14071     /**
14072      * Get the indexes of the selected nodes.
14073      * @return {Array}
14074      */
14075     getSelectedIndexes : function(){
14076         var indexes = [], s = this.selections;
14077         for(var i = 0, len = s.length; i < len; i++){
14078             indexes.push(s[i].nodeIndex);
14079         }
14080         return indexes;
14081     },
14082
14083     /**
14084      * Clear all selections
14085      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14086      */
14087     clearSelections : function(suppressEvent){
14088         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14089             this.cmp.elements = this.selections;
14090             this.cmp.removeClass(this.selectedClass);
14091             this.selections = [];
14092             if(!suppressEvent){
14093                 this.fireEvent("selectionchange", this, this.selections);
14094             }
14095         }
14096     },
14097
14098     /**
14099      * Returns true if the passed node is selected
14100      * @param {HTMLElement/Number} node The node or node index
14101      * @return {Boolean}
14102      */
14103     isSelected : function(node){
14104         var s = this.selections;
14105         if(s.length < 1){
14106             return false;
14107         }
14108         node = this.getNode(node);
14109         return s.indexOf(node) !== -1;
14110     },
14111
14112     /**
14113      * Selects nodes.
14114      * @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
14115      * @param {Boolean} keepExisting (optional) true to keep existing selections
14116      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14117      */
14118     select : function(nodeInfo, keepExisting, suppressEvent){
14119         if(nodeInfo instanceof Array){
14120             if(!keepExisting){
14121                 this.clearSelections(true);
14122             }
14123             for(var i = 0, len = nodeInfo.length; i < len; i++){
14124                 this.select(nodeInfo[i], true, true);
14125             }
14126             return;
14127         } 
14128         var node = this.getNode(nodeInfo);
14129         if(!node || this.isSelected(node)){
14130             return; // already selected.
14131         }
14132         if(!keepExisting){
14133             this.clearSelections(true);
14134         }
14135         
14136         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14137             Roo.fly(node).addClass(this.selectedClass);
14138             this.selections.push(node);
14139             if(!suppressEvent){
14140                 this.fireEvent("selectionchange", this, this.selections);
14141             }
14142         }
14143         
14144         
14145     },
14146       /**
14147      * Unselects nodes.
14148      * @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
14149      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14150      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14151      */
14152     unselect : function(nodeInfo, keepExisting, suppressEvent)
14153     {
14154         if(nodeInfo instanceof Array){
14155             Roo.each(this.selections, function(s) {
14156                 this.unselect(s, nodeInfo);
14157             }, this);
14158             return;
14159         }
14160         var node = this.getNode(nodeInfo);
14161         if(!node || !this.isSelected(node)){
14162             //Roo.log("not selected");
14163             return; // not selected.
14164         }
14165         // fireevent???
14166         var ns = [];
14167         Roo.each(this.selections, function(s) {
14168             if (s == node ) {
14169                 Roo.fly(node).removeClass(this.selectedClass);
14170
14171                 return;
14172             }
14173             ns.push(s);
14174         },this);
14175         
14176         this.selections= ns;
14177         this.fireEvent("selectionchange", this, this.selections);
14178     },
14179
14180     /**
14181      * Gets a template node.
14182      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14183      * @return {HTMLElement} The node or null if it wasn't found
14184      */
14185     getNode : function(nodeInfo){
14186         if(typeof nodeInfo == "string"){
14187             return document.getElementById(nodeInfo);
14188         }else if(typeof nodeInfo == "number"){
14189             return this.nodes[nodeInfo];
14190         }
14191         return nodeInfo;
14192     },
14193
14194     /**
14195      * Gets a range template nodes.
14196      * @param {Number} startIndex
14197      * @param {Number} endIndex
14198      * @return {Array} An array of nodes
14199      */
14200     getNodes : function(start, end){
14201         var ns = this.nodes;
14202         start = start || 0;
14203         end = typeof end == "undefined" ? ns.length - 1 : end;
14204         var nodes = [];
14205         if(start <= end){
14206             for(var i = start; i <= end; i++){
14207                 nodes.push(ns[i]);
14208             }
14209         } else{
14210             for(var i = start; i >= end; i--){
14211                 nodes.push(ns[i]);
14212             }
14213         }
14214         return nodes;
14215     },
14216
14217     /**
14218      * Finds the index of the passed node
14219      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14220      * @return {Number} The index of the node or -1
14221      */
14222     indexOf : function(node){
14223         node = this.getNode(node);
14224         if(typeof node.nodeIndex == "number"){
14225             return node.nodeIndex;
14226         }
14227         var ns = this.nodes;
14228         for(var i = 0, len = ns.length; i < len; i++){
14229             if(ns[i] == node){
14230                 return i;
14231             }
14232         }
14233         return -1;
14234     }
14235 });
14236 /*
14237  * - LGPL
14238  *
14239  * based on jquery fullcalendar
14240  * 
14241  */
14242
14243 Roo.bootstrap = Roo.bootstrap || {};
14244 /**
14245  * @class Roo.bootstrap.Calendar
14246  * @extends Roo.bootstrap.Component
14247  * Bootstrap Calendar class
14248  * @cfg {Boolean} loadMask (true|false) default false
14249  * @cfg {Object} header generate the user specific header of the calendar, default false
14250
14251  * @constructor
14252  * Create a new Container
14253  * @param {Object} config The config object
14254  */
14255
14256
14257
14258 Roo.bootstrap.Calendar = function(config){
14259     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14260      this.addEvents({
14261         /**
14262              * @event select
14263              * Fires when a date is selected
14264              * @param {DatePicker} this
14265              * @param {Date} date The selected date
14266              */
14267         'select': true,
14268         /**
14269              * @event monthchange
14270              * Fires when the displayed month changes 
14271              * @param {DatePicker} this
14272              * @param {Date} date The selected month
14273              */
14274         'monthchange': true,
14275         /**
14276              * @event evententer
14277              * Fires when mouse over an event
14278              * @param {Calendar} this
14279              * @param {event} Event
14280              */
14281         'evententer': true,
14282         /**
14283              * @event eventleave
14284              * Fires when the mouse leaves an
14285              * @param {Calendar} this
14286              * @param {event}
14287              */
14288         'eventleave': true,
14289         /**
14290              * @event eventclick
14291              * Fires when the mouse click an
14292              * @param {Calendar} this
14293              * @param {event}
14294              */
14295         'eventclick': true
14296         
14297     });
14298
14299 };
14300
14301 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14302     
14303      /**
14304      * @cfg {Number} startDay
14305      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14306      */
14307     startDay : 0,
14308     
14309     loadMask : false,
14310     
14311     header : false,
14312       
14313     getAutoCreate : function(){
14314         
14315         
14316         var fc_button = function(name, corner, style, content ) {
14317             return Roo.apply({},{
14318                 tag : 'span',
14319                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14320                          (corner.length ?
14321                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14322                             ''
14323                         ),
14324                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14325                 unselectable: 'on'
14326             });
14327         };
14328         
14329         var header = {};
14330         
14331         if(!this.header){
14332             header = {
14333                 tag : 'table',
14334                 cls : 'fc-header',
14335                 style : 'width:100%',
14336                 cn : [
14337                     {
14338                         tag: 'tr',
14339                         cn : [
14340                             {
14341                                 tag : 'td',
14342                                 cls : 'fc-header-left',
14343                                 cn : [
14344                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14345                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14346                                     { tag: 'span', cls: 'fc-header-space' },
14347                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14348
14349
14350                                 ]
14351                             },
14352
14353                             {
14354                                 tag : 'td',
14355                                 cls : 'fc-header-center',
14356                                 cn : [
14357                                     {
14358                                         tag: 'span',
14359                                         cls: 'fc-header-title',
14360                                         cn : {
14361                                             tag: 'H2',
14362                                             html : 'month / year'
14363                                         }
14364                                     }
14365
14366                                 ]
14367                             },
14368                             {
14369                                 tag : 'td',
14370                                 cls : 'fc-header-right',
14371                                 cn : [
14372                               /*      fc_button('month', 'left', '', 'month' ),
14373                                     fc_button('week', '', '', 'week' ),
14374                                     fc_button('day', 'right', '', 'day' )
14375                                 */    
14376
14377                                 ]
14378                             }
14379
14380                         ]
14381                     }
14382                 ]
14383             };
14384         }
14385         
14386         header = this.header;
14387         
14388        
14389         var cal_heads = function() {
14390             var ret = [];
14391             // fixme - handle this.
14392             
14393             for (var i =0; i < Date.dayNames.length; i++) {
14394                 var d = Date.dayNames[i];
14395                 ret.push({
14396                     tag: 'th',
14397                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14398                     html : d.substring(0,3)
14399                 });
14400                 
14401             }
14402             ret[0].cls += ' fc-first';
14403             ret[6].cls += ' fc-last';
14404             return ret;
14405         };
14406         var cal_cell = function(n) {
14407             return  {
14408                 tag: 'td',
14409                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14410                 cn : [
14411                     {
14412                         cn : [
14413                             {
14414                                 cls: 'fc-day-number',
14415                                 html: 'D'
14416                             },
14417                             {
14418                                 cls: 'fc-day-content',
14419                              
14420                                 cn : [
14421                                      {
14422                                         style: 'position: relative;' // height: 17px;
14423                                     }
14424                                 ]
14425                             }
14426                             
14427                             
14428                         ]
14429                     }
14430                 ]
14431                 
14432             }
14433         };
14434         var cal_rows = function() {
14435             
14436             var ret = [];
14437             for (var r = 0; r < 6; r++) {
14438                 var row= {
14439                     tag : 'tr',
14440                     cls : 'fc-week',
14441                     cn : []
14442                 };
14443                 
14444                 for (var i =0; i < Date.dayNames.length; i++) {
14445                     var d = Date.dayNames[i];
14446                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14447
14448                 }
14449                 row.cn[0].cls+=' fc-first';
14450                 row.cn[0].cn[0].style = 'min-height:90px';
14451                 row.cn[6].cls+=' fc-last';
14452                 ret.push(row);
14453                 
14454             }
14455             ret[0].cls += ' fc-first';
14456             ret[4].cls += ' fc-prev-last';
14457             ret[5].cls += ' fc-last';
14458             return ret;
14459             
14460         };
14461         
14462         var cal_table = {
14463             tag: 'table',
14464             cls: 'fc-border-separate',
14465             style : 'width:100%',
14466             cellspacing  : 0,
14467             cn : [
14468                 { 
14469                     tag: 'thead',
14470                     cn : [
14471                         { 
14472                             tag: 'tr',
14473                             cls : 'fc-first fc-last',
14474                             cn : cal_heads()
14475                         }
14476                     ]
14477                 },
14478                 { 
14479                     tag: 'tbody',
14480                     cn : cal_rows()
14481                 }
14482                   
14483             ]
14484         };
14485          
14486          var cfg = {
14487             cls : 'fc fc-ltr',
14488             cn : [
14489                 header,
14490                 {
14491                     cls : 'fc-content',
14492                     style : "position: relative;",
14493                     cn : [
14494                         {
14495                             cls : 'fc-view fc-view-month fc-grid',
14496                             style : 'position: relative',
14497                             unselectable : 'on',
14498                             cn : [
14499                                 {
14500                                     cls : 'fc-event-container',
14501                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14502                                 },
14503                                 cal_table
14504                             ]
14505                         }
14506                     ]
14507     
14508                 }
14509            ] 
14510             
14511         };
14512         
14513          
14514         
14515         return cfg;
14516     },
14517     
14518     
14519     initEvents : function()
14520     {
14521         if(!this.store){
14522             throw "can not find store for calendar";
14523         }
14524         
14525         var mark = {
14526             tag: "div",
14527             cls:"x-dlg-mask",
14528             style: "text-align:center",
14529             cn: [
14530                 {
14531                     tag: "div",
14532                     style: "background-color:white;width:50%;margin:250 auto",
14533                     cn: [
14534                         {
14535                             tag: "img",
14536                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14537                         },
14538                         {
14539                             tag: "span",
14540                             html: "Loading"
14541                         }
14542                         
14543                     ]
14544                 }
14545             ]
14546         }
14547         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14548         
14549         var size = this.el.select('.fc-content', true).first().getSize();
14550         this.maskEl.setSize(size.width, size.height);
14551         this.maskEl.enableDisplayMode("block");
14552         if(!this.loadMask){
14553             this.maskEl.hide();
14554         }
14555         
14556         this.store = Roo.factory(this.store, Roo.data);
14557         this.store.on('load', this.onLoad, this);
14558         this.store.on('beforeload', this.onBeforeLoad, this);
14559         
14560         this.resize();
14561         
14562         this.cells = this.el.select('.fc-day',true);
14563         //Roo.log(this.cells);
14564         this.textNodes = this.el.query('.fc-day-number');
14565         this.cells.addClassOnOver('fc-state-hover');
14566         
14567         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14568         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14569         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14570         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14571         
14572         this.on('monthchange', this.onMonthChange, this);
14573         
14574         this.update(new Date().clearTime());
14575     },
14576     
14577     resize : function() {
14578         var sz  = this.el.getSize();
14579         
14580         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14581         this.el.select('.fc-day-content div',true).setHeight(34);
14582     },
14583     
14584     
14585     // private
14586     showPrevMonth : function(e){
14587         this.update(this.activeDate.add("mo", -1));
14588     },
14589     showToday : function(e){
14590         this.update(new Date().clearTime());
14591     },
14592     // private
14593     showNextMonth : function(e){
14594         this.update(this.activeDate.add("mo", 1));
14595     },
14596
14597     // private
14598     showPrevYear : function(){
14599         this.update(this.activeDate.add("y", -1));
14600     },
14601
14602     // private
14603     showNextYear : function(){
14604         this.update(this.activeDate.add("y", 1));
14605     },
14606
14607     
14608    // private
14609     update : function(date)
14610     {
14611         var vd = this.activeDate;
14612         this.activeDate = date;
14613 //        if(vd && this.el){
14614 //            var t = date.getTime();
14615 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14616 //                Roo.log('using add remove');
14617 //                
14618 //                this.fireEvent('monthchange', this, date);
14619 //                
14620 //                this.cells.removeClass("fc-state-highlight");
14621 //                this.cells.each(function(c){
14622 //                   if(c.dateValue == t){
14623 //                       c.addClass("fc-state-highlight");
14624 //                       setTimeout(function(){
14625 //                            try{c.dom.firstChild.focus();}catch(e){}
14626 //                       }, 50);
14627 //                       return false;
14628 //                   }
14629 //                   return true;
14630 //                });
14631 //                return;
14632 //            }
14633 //        }
14634         
14635         var days = date.getDaysInMonth();
14636         
14637         var firstOfMonth = date.getFirstDateOfMonth();
14638         var startingPos = firstOfMonth.getDay()-this.startDay;
14639         
14640         if(startingPos < this.startDay){
14641             startingPos += 7;
14642         }
14643         
14644         var pm = date.add(Date.MONTH, -1);
14645         var prevStart = pm.getDaysInMonth()-startingPos;
14646 //        
14647         this.cells = this.el.select('.fc-day',true);
14648         this.textNodes = this.el.query('.fc-day-number');
14649         this.cells.addClassOnOver('fc-state-hover');
14650         
14651         var cells = this.cells.elements;
14652         var textEls = this.textNodes;
14653         
14654         Roo.each(cells, function(cell){
14655             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14656         });
14657         
14658         days += startingPos;
14659
14660         // convert everything to numbers so it's fast
14661         var day = 86400000;
14662         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14663         //Roo.log(d);
14664         //Roo.log(pm);
14665         //Roo.log(prevStart);
14666         
14667         var today = new Date().clearTime().getTime();
14668         var sel = date.clearTime().getTime();
14669         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14670         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14671         var ddMatch = this.disabledDatesRE;
14672         var ddText = this.disabledDatesText;
14673         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14674         var ddaysText = this.disabledDaysText;
14675         var format = this.format;
14676         
14677         var setCellClass = function(cal, cell){
14678             cell.row = 0;
14679             cell.events = [];
14680             cell.more = [];
14681             //Roo.log('set Cell Class');
14682             cell.title = "";
14683             var t = d.getTime();
14684             
14685             //Roo.log(d);
14686             
14687             cell.dateValue = t;
14688             if(t == today){
14689                 cell.className += " fc-today";
14690                 cell.className += " fc-state-highlight";
14691                 cell.title = cal.todayText;
14692             }
14693             if(t == sel){
14694                 // disable highlight in other month..
14695                 //cell.className += " fc-state-highlight";
14696                 
14697             }
14698             // disabling
14699             if(t < min) {
14700                 cell.className = " fc-state-disabled";
14701                 cell.title = cal.minText;
14702                 return;
14703             }
14704             if(t > max) {
14705                 cell.className = " fc-state-disabled";
14706                 cell.title = cal.maxText;
14707                 return;
14708             }
14709             if(ddays){
14710                 if(ddays.indexOf(d.getDay()) != -1){
14711                     cell.title = ddaysText;
14712                     cell.className = " fc-state-disabled";
14713                 }
14714             }
14715             if(ddMatch && format){
14716                 var fvalue = d.dateFormat(format);
14717                 if(ddMatch.test(fvalue)){
14718                     cell.title = ddText.replace("%0", fvalue);
14719                     cell.className = " fc-state-disabled";
14720                 }
14721             }
14722             
14723             if (!cell.initialClassName) {
14724                 cell.initialClassName = cell.dom.className;
14725             }
14726             
14727             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14728         };
14729
14730         var i = 0;
14731         
14732         for(; i < startingPos; i++) {
14733             textEls[i].innerHTML = (++prevStart);
14734             d.setDate(d.getDate()+1);
14735             
14736             cells[i].className = "fc-past fc-other-month";
14737             setCellClass(this, cells[i]);
14738         }
14739         
14740         var intDay = 0;
14741         
14742         for(; i < days; i++){
14743             intDay = i - startingPos + 1;
14744             textEls[i].innerHTML = (intDay);
14745             d.setDate(d.getDate()+1);
14746             
14747             cells[i].className = ''; // "x-date-active";
14748             setCellClass(this, cells[i]);
14749         }
14750         var extraDays = 0;
14751         
14752         for(; i < 42; i++) {
14753             textEls[i].innerHTML = (++extraDays);
14754             d.setDate(d.getDate()+1);
14755             
14756             cells[i].className = "fc-future fc-other-month";
14757             setCellClass(this, cells[i]);
14758         }
14759         
14760         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14761         
14762         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14763         
14764         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14765         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14766         
14767         if(totalRows != 6){
14768             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14769             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14770         }
14771         
14772         this.fireEvent('monthchange', this, date);
14773         
14774         
14775         /*
14776         if(!this.internalRender){
14777             var main = this.el.dom.firstChild;
14778             var w = main.offsetWidth;
14779             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14780             Roo.fly(main).setWidth(w);
14781             this.internalRender = true;
14782             // opera does not respect the auto grow header center column
14783             // then, after it gets a width opera refuses to recalculate
14784             // without a second pass
14785             if(Roo.isOpera && !this.secondPass){
14786                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14787                 this.secondPass = true;
14788                 this.update.defer(10, this, [date]);
14789             }
14790         }
14791         */
14792         
14793     },
14794     
14795     findCell : function(dt) {
14796         dt = dt.clearTime().getTime();
14797         var ret = false;
14798         this.cells.each(function(c){
14799             //Roo.log("check " +c.dateValue + '?=' + dt);
14800             if(c.dateValue == dt){
14801                 ret = c;
14802                 return false;
14803             }
14804             return true;
14805         });
14806         
14807         return ret;
14808     },
14809     
14810     findCells : function(ev) {
14811         var s = ev.start.clone().clearTime().getTime();
14812        // Roo.log(s);
14813         var e= ev.end.clone().clearTime().getTime();
14814        // Roo.log(e);
14815         var ret = [];
14816         this.cells.each(function(c){
14817              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14818             
14819             if(c.dateValue > e){
14820                 return ;
14821             }
14822             if(c.dateValue < s){
14823                 return ;
14824             }
14825             ret.push(c);
14826         });
14827         
14828         return ret;    
14829     },
14830     
14831 //    findBestRow: function(cells)
14832 //    {
14833 //        var ret = 0;
14834 //        
14835 //        for (var i =0 ; i < cells.length;i++) {
14836 //            ret  = Math.max(cells[i].rows || 0,ret);
14837 //        }
14838 //        return ret;
14839 //        
14840 //    },
14841     
14842     
14843     addItem : function(ev)
14844     {
14845         // look for vertical location slot in
14846         var cells = this.findCells(ev);
14847         
14848 //        ev.row = this.findBestRow(cells);
14849         
14850         // work out the location.
14851         
14852         var crow = false;
14853         var rows = [];
14854         for(var i =0; i < cells.length; i++) {
14855             
14856             cells[i].row = cells[0].row;
14857             
14858             if(i == 0){
14859                 cells[i].row = cells[i].row + 1;
14860             }
14861             
14862             if (!crow) {
14863                 crow = {
14864                     start : cells[i],
14865                     end :  cells[i]
14866                 };
14867                 continue;
14868             }
14869             if (crow.start.getY() == cells[i].getY()) {
14870                 // on same row.
14871                 crow.end = cells[i];
14872                 continue;
14873             }
14874             // different row.
14875             rows.push(crow);
14876             crow = {
14877                 start: cells[i],
14878                 end : cells[i]
14879             };
14880             
14881         }
14882         
14883         rows.push(crow);
14884         ev.els = [];
14885         ev.rows = rows;
14886         ev.cells = cells;
14887         
14888         cells[0].events.push(ev);
14889         
14890         this.calevents.push(ev);
14891     },
14892     
14893     clearEvents: function() {
14894         
14895         if(!this.calevents){
14896             return;
14897         }
14898         
14899         Roo.each(this.cells.elements, function(c){
14900             c.row = 0;
14901             c.events = [];
14902             c.more = [];
14903         });
14904         
14905         Roo.each(this.calevents, function(e) {
14906             Roo.each(e.els, function(el) {
14907                 el.un('mouseenter' ,this.onEventEnter, this);
14908                 el.un('mouseleave' ,this.onEventLeave, this);
14909                 el.remove();
14910             },this);
14911         },this);
14912         
14913         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
14914             e.remove();
14915         });
14916         
14917     },
14918     
14919     renderEvents: function()
14920     {   
14921         var _this = this;
14922         
14923         this.cells.each(function(c) {
14924             
14925             if(c.row < 5){
14926                 return;
14927             }
14928             
14929             var ev = c.events;
14930             
14931             var r = 4;
14932             if(c.row != c.events.length){
14933                 r = 4 - (4 - (c.row - c.events.length));
14934             }
14935             
14936             c.events = ev.slice(0, r);
14937             c.more = ev.slice(r);
14938             
14939             if(c.more.length && c.more.length == 1){
14940                 c.events.push(c.more.pop());
14941             }
14942             
14943             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14944             
14945         });
14946             
14947         this.cells.each(function(c) {
14948             
14949             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14950             
14951             
14952             for (var e = 0; e < c.events.length; e++){
14953                 var ev = c.events[e];
14954                 var rows = ev.rows;
14955                 
14956                 for(var i = 0; i < rows.length; i++) {
14957                 
14958                     // how many rows should it span..
14959
14960                     var  cfg = {
14961                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14962                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14963
14964                         unselectable : "on",
14965                         cn : [
14966                             {
14967                                 cls: 'fc-event-inner',
14968                                 cn : [
14969     //                                {
14970     //                                  tag:'span',
14971     //                                  cls: 'fc-event-time',
14972     //                                  html : cells.length > 1 ? '' : ev.time
14973     //                                },
14974                                     {
14975                                       tag:'span',
14976                                       cls: 'fc-event-title',
14977                                       html : String.format('{0}', ev.title)
14978                                     }
14979
14980
14981                                 ]
14982                             },
14983                             {
14984                                 cls: 'ui-resizable-handle ui-resizable-e',
14985                                 html : '&nbsp;&nbsp;&nbsp'
14986                             }
14987
14988                         ]
14989                     };
14990
14991                     if (i == 0) {
14992                         cfg.cls += ' fc-event-start';
14993                     }
14994                     if ((i+1) == rows.length) {
14995                         cfg.cls += ' fc-event-end';
14996                     }
14997
14998                     var ctr = _this.el.select('.fc-event-container',true).first();
14999                     var cg = ctr.createChild(cfg);
15000
15001                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15002                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15003
15004                     var r = (c.more.length) ? 1 : 0;
15005                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15006                     cg.setWidth(ebox.right - sbox.x -2);
15007
15008                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15009                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15010                     cg.on('click', _this.onEventClick, _this, ev);
15011
15012                     ev.els.push(cg);
15013                     
15014                 }
15015                 
15016             }
15017             
15018             
15019             if(c.more.length){
15020                 var  cfg = {
15021                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15022                     style : 'position: absolute',
15023                     unselectable : "on",
15024                     cn : [
15025                         {
15026                             cls: 'fc-event-inner',
15027                             cn : [
15028                                 {
15029                                   tag:'span',
15030                                   cls: 'fc-event-title',
15031                                   html : 'More'
15032                                 }
15033
15034
15035                             ]
15036                         },
15037                         {
15038                             cls: 'ui-resizable-handle ui-resizable-e',
15039                             html : '&nbsp;&nbsp;&nbsp'
15040                         }
15041
15042                     ]
15043                 };
15044
15045                 var ctr = _this.el.select('.fc-event-container',true).first();
15046                 var cg = ctr.createChild(cfg);
15047
15048                 var sbox = c.select('.fc-day-content',true).first().getBox();
15049                 var ebox = c.select('.fc-day-content',true).first().getBox();
15050                 //Roo.log(cg);
15051                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15052                 cg.setWidth(ebox.right - sbox.x -2);
15053
15054                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15055                 
15056             }
15057             
15058         });
15059         
15060         
15061         
15062     },
15063     
15064     onEventEnter: function (e, el,event,d) {
15065         this.fireEvent('evententer', this, el, event);
15066     },
15067     
15068     onEventLeave: function (e, el,event,d) {
15069         this.fireEvent('eventleave', this, el, event);
15070     },
15071     
15072     onEventClick: function (e, el,event,d) {
15073         this.fireEvent('eventclick', this, el, event);
15074     },
15075     
15076     onMonthChange: function () {
15077         this.store.load();
15078     },
15079     
15080     onMoreEventClick: function(e, el, more)
15081     {
15082         var _this = this;
15083         
15084         this.calpopover.placement = 'right';
15085         this.calpopover.setTitle('More');
15086         
15087         this.calpopover.setContent('');
15088         
15089         var ctr = this.calpopover.el.select('.popover-content', true).first();
15090         
15091         Roo.each(more, function(m){
15092             var cfg = {
15093                 cls : 'fc-event-hori fc-event-draggable',
15094                 html : m.title
15095             }
15096             var cg = ctr.createChild(cfg);
15097             
15098             cg.on('click', _this.onEventClick, _this, m);
15099         });
15100         
15101         this.calpopover.show(el);
15102         
15103         
15104     },
15105     
15106     onLoad: function () 
15107     {   
15108         this.calevents = [];
15109         var cal = this;
15110         
15111         if(this.store.getCount() > 0){
15112             this.store.data.each(function(d){
15113                cal.addItem({
15114                     id : d.data.id,
15115                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15116                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15117                     time : d.data.start_time,
15118                     title : d.data.title,
15119                     description : d.data.description,
15120                     venue : d.data.venue
15121                 });
15122             });
15123         }
15124         
15125         this.renderEvents();
15126         
15127         if(this.calevents.length && this.loadMask){
15128             this.maskEl.hide();
15129         }
15130     },
15131     
15132     onBeforeLoad: function()
15133     {
15134         this.clearEvents();
15135         if(this.loadMask){
15136             this.maskEl.show();
15137         }
15138     }
15139 });
15140
15141  
15142  /*
15143  * - LGPL
15144  *
15145  * element
15146  * 
15147  */
15148
15149 /**
15150  * @class Roo.bootstrap.Popover
15151  * @extends Roo.bootstrap.Component
15152  * Bootstrap Popover class
15153  * @cfg {String} html contents of the popover   (or false to use children..)
15154  * @cfg {String} title of popover (or false to hide)
15155  * @cfg {String} placement how it is placed
15156  * @cfg {String} trigger click || hover (or false to trigger manually)
15157  * @cfg {String} over what (parent or false to trigger manually.)
15158  * @cfg {Number} delay - delay before showing
15159  
15160  * @constructor
15161  * Create a new Popover
15162  * @param {Object} config The config object
15163  */
15164
15165 Roo.bootstrap.Popover = function(config){
15166     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15167 };
15168
15169 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15170     
15171     title: 'Fill in a title',
15172     html: false,
15173     
15174     placement : 'right',
15175     trigger : 'hover', // hover
15176     
15177     delay : 0,
15178     
15179     over: 'parent',
15180     
15181     can_build_overlaid : false,
15182     
15183     getChildContainer : function()
15184     {
15185         return this.el.select('.popover-content',true).first();
15186     },
15187     
15188     getAutoCreate : function(){
15189          Roo.log('make popover?');
15190         var cfg = {
15191            cls : 'popover roo-dynamic',
15192            style: 'display:block',
15193            cn : [
15194                 {
15195                     cls : 'arrow'
15196                 },
15197                 {
15198                     cls : 'popover-inner',
15199                     cn : [
15200                         {
15201                             tag: 'h3',
15202                             cls: 'popover-title',
15203                             html : this.title
15204                         },
15205                         {
15206                             cls : 'popover-content',
15207                             html : this.html
15208                         }
15209                     ]
15210                     
15211                 }
15212            ]
15213         };
15214         
15215         return cfg;
15216     },
15217     setTitle: function(str)
15218     {
15219         this.title = str;
15220         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15221     },
15222     setContent: function(str)
15223     {
15224         this.html = str;
15225         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15226     },
15227     // as it get's added to the bottom of the page.
15228     onRender : function(ct, position)
15229     {
15230         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15231         if(!this.el){
15232             var cfg = Roo.apply({},  this.getAutoCreate());
15233             cfg.id = Roo.id();
15234             
15235             if (this.cls) {
15236                 cfg.cls += ' ' + this.cls;
15237             }
15238             if (this.style) {
15239                 cfg.style = this.style;
15240             }
15241             Roo.log("adding to ")
15242             this.el = Roo.get(document.body).createChild(cfg, position);
15243             Roo.log(this.el);
15244         }
15245         this.initEvents();
15246     },
15247     
15248     initEvents : function()
15249     {
15250         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15251         this.el.enableDisplayMode('block');
15252         this.el.hide();
15253         if (this.over === false) {
15254             return; 
15255         }
15256         if (this.triggers === false) {
15257             return;
15258         }
15259         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15260         var triggers = this.trigger ? this.trigger.split(' ') : [];
15261         Roo.each(triggers, function(trigger) {
15262         
15263             if (trigger == 'click') {
15264                 on_el.on('click', this.toggle, this);
15265             } else if (trigger != 'manual') {
15266                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15267                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15268       
15269                 on_el.on(eventIn  ,this.enter, this);
15270                 on_el.on(eventOut, this.leave, this);
15271             }
15272         }, this);
15273         
15274     },
15275     
15276     
15277     // private
15278     timeout : null,
15279     hoverState : null,
15280     
15281     toggle : function () {
15282         this.hoverState == 'in' ? this.leave() : this.enter();
15283     },
15284     
15285     enter : function () {
15286        
15287     
15288         clearTimeout(this.timeout);
15289     
15290         this.hoverState = 'in';
15291     
15292         if (!this.delay || !this.delay.show) {
15293             this.show();
15294             return;
15295         }
15296         var _t = this;
15297         this.timeout = setTimeout(function () {
15298             if (_t.hoverState == 'in') {
15299                 _t.show();
15300             }
15301         }, this.delay.show)
15302     },
15303     leave : function() {
15304         clearTimeout(this.timeout);
15305     
15306         this.hoverState = 'out';
15307     
15308         if (!this.delay || !this.delay.hide) {
15309             this.hide();
15310             return;
15311         }
15312         var _t = this;
15313         this.timeout = setTimeout(function () {
15314             if (_t.hoverState == 'out') {
15315                 _t.hide();
15316             }
15317         }, this.delay.hide)
15318     },
15319     
15320     show : function (on_el)
15321     {
15322         if (!on_el) {
15323             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15324         }
15325         // set content.
15326         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15327         if (this.html !== false) {
15328             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15329         }
15330         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15331         if (!this.title.length) {
15332             this.el.select('.popover-title',true).hide();
15333         }
15334         
15335         var placement = typeof this.placement == 'function' ?
15336             this.placement.call(this, this.el, on_el) :
15337             this.placement;
15338             
15339         var autoToken = /\s?auto?\s?/i;
15340         var autoPlace = autoToken.test(placement);
15341         if (autoPlace) {
15342             placement = placement.replace(autoToken, '') || 'top';
15343         }
15344         
15345         //this.el.detach()
15346         //this.el.setXY([0,0]);
15347         this.el.show();
15348         this.el.dom.style.display='block';
15349         this.el.addClass(placement);
15350         
15351         //this.el.appendTo(on_el);
15352         
15353         var p = this.getPosition();
15354         var box = this.el.getBox();
15355         
15356         if (autoPlace) {
15357             // fixme..
15358         }
15359         var align = Roo.bootstrap.Popover.alignment[placement];
15360         this.el.alignTo(on_el, align[0],align[1]);
15361         //var arrow = this.el.select('.arrow',true).first();
15362         //arrow.set(align[2], 
15363         
15364         this.el.addClass('in');
15365         
15366         
15367         if (this.el.hasClass('fade')) {
15368             // fade it?
15369         }
15370         
15371     },
15372     hide : function()
15373     {
15374         this.el.setXY([0,0]);
15375         this.el.removeClass('in');
15376         this.el.hide();
15377         this.hoverState = null;
15378         
15379     }
15380     
15381 });
15382
15383 Roo.bootstrap.Popover.alignment = {
15384     'left' : ['r-l', [-10,0], 'right'],
15385     'right' : ['l-r', [10,0], 'left'],
15386     'bottom' : ['t-b', [0,10], 'top'],
15387     'top' : [ 'b-t', [0,-10], 'bottom']
15388 };
15389
15390  /*
15391  * - LGPL
15392  *
15393  * Progress
15394  * 
15395  */
15396
15397 /**
15398  * @class Roo.bootstrap.Progress
15399  * @extends Roo.bootstrap.Component
15400  * Bootstrap Progress class
15401  * @cfg {Boolean} striped striped of the progress bar
15402  * @cfg {Boolean} active animated of the progress bar
15403  * 
15404  * 
15405  * @constructor
15406  * Create a new Progress
15407  * @param {Object} config The config object
15408  */
15409
15410 Roo.bootstrap.Progress = function(config){
15411     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15412 };
15413
15414 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15415     
15416     striped : false,
15417     active: false,
15418     
15419     getAutoCreate : function(){
15420         var cfg = {
15421             tag: 'div',
15422             cls: 'progress'
15423         };
15424         
15425         
15426         if(this.striped){
15427             cfg.cls += ' progress-striped';
15428         }
15429       
15430         if(this.active){
15431             cfg.cls += ' active';
15432         }
15433         
15434         
15435         return cfg;
15436     }
15437    
15438 });
15439
15440  
15441
15442  /*
15443  * - LGPL
15444  *
15445  * ProgressBar
15446  * 
15447  */
15448
15449 /**
15450  * @class Roo.bootstrap.ProgressBar
15451  * @extends Roo.bootstrap.Component
15452  * Bootstrap ProgressBar class
15453  * @cfg {Number} aria_valuenow aria-value now
15454  * @cfg {Number} aria_valuemin aria-value min
15455  * @cfg {Number} aria_valuemax aria-value max
15456  * @cfg {String} label label for the progress bar
15457  * @cfg {String} panel (success | info | warning | danger )
15458  * @cfg {String} role role of the progress bar
15459  * @cfg {String} sr_only text
15460  * 
15461  * 
15462  * @constructor
15463  * Create a new ProgressBar
15464  * @param {Object} config The config object
15465  */
15466
15467 Roo.bootstrap.ProgressBar = function(config){
15468     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15469 };
15470
15471 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15472     
15473     aria_valuenow : 0,
15474     aria_valuemin : 0,
15475     aria_valuemax : 100,
15476     label : false,
15477     panel : false,
15478     role : false,
15479     sr_only: false,
15480     
15481     getAutoCreate : function()
15482     {
15483         
15484         var cfg = {
15485             tag: 'div',
15486             cls: 'progress-bar',
15487             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15488         };
15489         
15490         if(this.sr_only){
15491             cfg.cn = {
15492                 tag: 'span',
15493                 cls: 'sr-only',
15494                 html: this.sr_only
15495             }
15496         }
15497         
15498         if(this.role){
15499             cfg.role = this.role;
15500         }
15501         
15502         if(this.aria_valuenow){
15503             cfg['aria-valuenow'] = this.aria_valuenow;
15504         }
15505         
15506         if(this.aria_valuemin){
15507             cfg['aria-valuemin'] = this.aria_valuemin;
15508         }
15509         
15510         if(this.aria_valuemax){
15511             cfg['aria-valuemax'] = this.aria_valuemax;
15512         }
15513         
15514         if(this.label && !this.sr_only){
15515             cfg.html = this.label;
15516         }
15517         
15518         if(this.panel){
15519             cfg.cls += ' progress-bar-' + this.panel;
15520         }
15521         
15522         return cfg;
15523     },
15524     
15525     update : function(aria_valuenow)
15526     {
15527         this.aria_valuenow = aria_valuenow;
15528         
15529         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15530     }
15531    
15532 });
15533
15534  
15535
15536  /*
15537  * - LGPL
15538  *
15539  * column
15540  * 
15541  */
15542
15543 /**
15544  * @class Roo.bootstrap.TabGroup
15545  * @extends Roo.bootstrap.Column
15546  * Bootstrap Column class
15547  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15548  * @cfg {Boolean} carousel true to make the group behave like a carousel
15549  * @cfg {Number} bullets show the panel pointer.. default 0
15550  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15551  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15552  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15553  * 
15554  * @constructor
15555  * Create a new TabGroup
15556  * @param {Object} config The config object
15557  */
15558
15559 Roo.bootstrap.TabGroup = function(config){
15560     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15561     if (!this.navId) {
15562         this.navId = Roo.id();
15563     }
15564     this.tabs = [];
15565     Roo.bootstrap.TabGroup.register(this);
15566     
15567 };
15568
15569 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15570     
15571     carousel : false,
15572     transition : false,
15573     bullets : 0,
15574     timer : 0,
15575     autoslide : false,
15576     slideFn : false,
15577     slideOnTouch : false,
15578     
15579     getAutoCreate : function()
15580     {
15581         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15582         
15583         cfg.cls += ' tab-content';
15584         
15585         Roo.log('get auto create...............');
15586         
15587         if (this.carousel) {
15588             cfg.cls += ' carousel slide';
15589             
15590             cfg.cn = [{
15591                cls : 'carousel-inner'
15592             }];
15593         
15594             if(this.bullets > 0 && !Roo.isTouch){
15595                 
15596                 var bullets = {
15597                     cls : 'carousel-bullets',
15598                     cn : []
15599                 };
15600                 
15601                 if(this.bullets_cls){
15602                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15603                 }
15604                 
15605                 for (var i = 0; i < this.bullets; i++){
15606                     bullets.cn.push({
15607                         cls : 'bullet bullet-' + i
15608                     });
15609                 }
15610                 
15611                 bullets.cn.push({
15612                     cls : 'clear'
15613                 });
15614                 
15615                 cfg.cn[0].cn = bullets;
15616             }
15617         }
15618         
15619         return cfg;
15620     },
15621     
15622     initEvents:  function()
15623     {
15624         Roo.log('-------- init events on tab group ---------');
15625         
15626         if(this.bullets > 0 && !Roo.isTouch){
15627             this.initBullet();
15628         }
15629         
15630         Roo.log(this);
15631         
15632         if(Roo.isTouch && this.slideOnTouch){
15633             this.el.on("touchstart", this.onTouchStart, this);
15634         }
15635         
15636         if(this.autoslide){
15637             var _this = this;
15638             
15639             this.slideFn = window.setInterval(function() {
15640                 _this.showPanelNext();
15641             }, this.timer);
15642         }
15643         
15644     },
15645     
15646     onTouchStart : function(e, el, o)
15647     {
15648         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15649             return;
15650         }
15651         
15652         this.showPanelNext();
15653     },
15654     
15655     getChildContainer : function()
15656     {
15657         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15658     },
15659     
15660     /**
15661     * register a Navigation item
15662     * @param {Roo.bootstrap.NavItem} the navitem to add
15663     */
15664     register : function(item)
15665     {
15666         this.tabs.push( item);
15667         item.navId = this.navId; // not really needed..
15668     
15669     },
15670     
15671     getActivePanel : function()
15672     {
15673         var r = false;
15674         Roo.each(this.tabs, function(t) {
15675             if (t.active) {
15676                 r = t;
15677                 return false;
15678             }
15679             return null;
15680         });
15681         return r;
15682         
15683     },
15684     getPanelByName : function(n)
15685     {
15686         var r = false;
15687         Roo.each(this.tabs, function(t) {
15688             if (t.tabId == n) {
15689                 r = t;
15690                 return false;
15691             }
15692             return null;
15693         });
15694         return r;
15695     },
15696     indexOfPanel : function(p)
15697     {
15698         var r = false;
15699         Roo.each(this.tabs, function(t,i) {
15700             if (t.tabId == p.tabId) {
15701                 r = i;
15702                 return false;
15703             }
15704             return null;
15705         });
15706         return r;
15707     },
15708     /**
15709      * show a specific panel
15710      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15711      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15712      */
15713     showPanel : function (pan)
15714     {
15715         if(this.transition){
15716             Roo.log("waiting for the transitionend");
15717             return;
15718         }
15719         
15720         if (typeof(pan) == 'number') {
15721             pan = this.tabs[pan];
15722         }
15723         if (typeof(pan) == 'string') {
15724             pan = this.getPanelByName(pan);
15725         }
15726         if (pan.tabId == this.getActivePanel().tabId) {
15727             return true;
15728         }
15729         var cur = this.getActivePanel();
15730         
15731         if (false === cur.fireEvent('beforedeactivate')) {
15732             return false;
15733         }
15734         
15735         if(this.bullets > 0 && !Roo.isTouch){
15736             this.setActiveBullet(this.indexOfPanel(pan));
15737         }
15738         
15739         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15740             
15741             this.transition = true;
15742             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15743             var lr = dir == 'next' ? 'left' : 'right';
15744             pan.el.addClass(dir); // or prev
15745             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15746             cur.el.addClass(lr); // or right
15747             pan.el.addClass(lr);
15748             
15749             var _this = this;
15750             cur.el.on('transitionend', function() {
15751                 Roo.log("trans end?");
15752                 
15753                 pan.el.removeClass([lr,dir]);
15754                 pan.setActive(true);
15755                 
15756                 cur.el.removeClass([lr]);
15757                 cur.setActive(false);
15758                 
15759                 _this.transition = false;
15760                 
15761             }, this, { single:  true } );
15762             
15763             return true;
15764         }
15765         
15766         cur.setActive(false);
15767         pan.setActive(true);
15768         
15769         return true;
15770         
15771     },
15772     showPanelNext : function()
15773     {
15774         var i = this.indexOfPanel(this.getActivePanel());
15775         
15776         if (i >= this.tabs.length - 1 && !this.autoslide) {
15777             return;
15778         }
15779         
15780         if (i >= this.tabs.length - 1 && this.autoslide) {
15781             i = -1;
15782         }
15783         
15784         this.showPanel(this.tabs[i+1]);
15785     },
15786     
15787     showPanelPrev : function()
15788     {
15789         var i = this.indexOfPanel(this.getActivePanel());
15790         
15791         if (i  < 1 && !this.autoslide) {
15792             return;
15793         }
15794         
15795         if (i < 1 && this.autoslide) {
15796             i = this.tabs.length;
15797         }
15798         
15799         this.showPanel(this.tabs[i-1]);
15800     },
15801     
15802     initBullet : function()
15803     {
15804         if(Roo.isTouch){
15805             return;
15806         }
15807         
15808         var _this = this;
15809         
15810         for (var i = 0; i < this.bullets; i++){
15811             var bullet = this.el.select('.bullet-' + i, true).first();
15812
15813             if(!bullet){
15814                 continue;
15815             }
15816
15817             bullet.on('click', (function(e, el, o, ii, t){
15818
15819                 e.preventDefault();
15820
15821                 _this.showPanel(ii);
15822
15823                 if(_this.autoslide && _this.slideFn){
15824                     clearInterval(_this.slideFn);
15825                     _this.slideFn = window.setInterval(function() {
15826                         _this.showPanelNext();
15827                     }, _this.timer);
15828                 }
15829
15830             }).createDelegate(this, [i, bullet], true));
15831         }
15832     },
15833     
15834     setActiveBullet : function(i)
15835     {
15836         if(Roo.isTouch){
15837             return;
15838         }
15839         
15840         Roo.each(this.el.select('.bullet', true).elements, function(el){
15841             el.removeClass('selected');
15842         });
15843
15844         var bullet = this.el.select('.bullet-' + i, true).first();
15845         
15846         if(!bullet){
15847             return;
15848         }
15849         
15850         bullet.addClass('selected');
15851     }
15852     
15853     
15854   
15855 });
15856
15857  
15858
15859  
15860  
15861 Roo.apply(Roo.bootstrap.TabGroup, {
15862     
15863     groups: {},
15864      /**
15865     * register a Navigation Group
15866     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15867     */
15868     register : function(navgrp)
15869     {
15870         this.groups[navgrp.navId] = navgrp;
15871         
15872     },
15873     /**
15874     * fetch a Navigation Group based on the navigation ID
15875     * if one does not exist , it will get created.
15876     * @param {string} the navgroup to add
15877     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15878     */
15879     get: function(navId) {
15880         if (typeof(this.groups[navId]) == 'undefined') {
15881             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15882         }
15883         return this.groups[navId] ;
15884     }
15885     
15886     
15887     
15888 });
15889
15890  /*
15891  * - LGPL
15892  *
15893  * TabPanel
15894  * 
15895  */
15896
15897 /**
15898  * @class Roo.bootstrap.TabPanel
15899  * @extends Roo.bootstrap.Component
15900  * Bootstrap TabPanel class
15901  * @cfg {Boolean} active panel active
15902  * @cfg {String} html panel content
15903  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
15904  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
15905  * 
15906  * 
15907  * @constructor
15908  * Create a new TabPanel
15909  * @param {Object} config The config object
15910  */
15911
15912 Roo.bootstrap.TabPanel = function(config){
15913     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
15914     this.addEvents({
15915         /**
15916              * @event changed
15917              * Fires when the active status changes
15918              * @param {Roo.bootstrap.TabPanel} this
15919              * @param {Boolean} state the new state
15920             
15921          */
15922         'changed': true,
15923         /**
15924              * @event beforedeactivate
15925              * Fires before a tab is de-activated - can be used to do validation on a form.
15926              * @param {Roo.bootstrap.TabPanel} this
15927              * @return {Boolean} false if there is an error
15928             
15929          */
15930         'beforedeactivate': true
15931      });
15932     
15933     this.tabId = this.tabId || Roo.id();
15934   
15935 };
15936
15937 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15938     
15939     active: false,
15940     html: false,
15941     tabId: false,
15942     navId : false,
15943     
15944     getAutoCreate : function(){
15945         var cfg = {
15946             tag: 'div',
15947             // item is needed for carousel - not sure if it has any effect otherwise
15948             cls: 'tab-pane item',
15949             html: this.html || ''
15950         };
15951         
15952         if(this.active){
15953             cfg.cls += ' active';
15954         }
15955         
15956         if(this.tabId){
15957             cfg.tabId = this.tabId;
15958         }
15959         
15960         
15961         return cfg;
15962     },
15963     
15964     initEvents:  function()
15965     {
15966         Roo.log('-------- init events on tab panel ---------');
15967         
15968         var p = this.parent();
15969         this.navId = this.navId || p.navId;
15970         
15971         if (typeof(this.navId) != 'undefined') {
15972             // not really needed.. but just in case.. parent should be a NavGroup.
15973             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15974             Roo.log(['register', tg, this]);
15975             tg.register(this);
15976             
15977             var i = tg.tabs.length - 1;
15978             
15979             if(this.active && tg.bullets > 0 && i < tg.bullets){
15980                 tg.setActiveBullet(i);
15981             }
15982         }
15983         
15984     },
15985     
15986     
15987     onRender : function(ct, position)
15988     {
15989        // Roo.log("Call onRender: " + this.xtype);
15990         
15991         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15992         
15993         
15994         
15995         
15996         
15997     },
15998     
15999     setActive: function(state)
16000     {
16001         Roo.log("panel - set active " + this.tabId + "=" + state);
16002         
16003         this.active = state;
16004         if (!state) {
16005             this.el.removeClass('active');
16006             
16007         } else  if (!this.el.hasClass('active')) {
16008             this.el.addClass('active');
16009         }
16010         
16011         this.fireEvent('changed', this, state);
16012     }
16013     
16014     
16015 });
16016  
16017
16018  
16019
16020  /*
16021  * - LGPL
16022  *
16023  * DateField
16024  * 
16025  */
16026
16027 /**
16028  * @class Roo.bootstrap.DateField
16029  * @extends Roo.bootstrap.Input
16030  * Bootstrap DateField class
16031  * @cfg {Number} weekStart default 0
16032  * @cfg {String} viewMode default empty, (months|years)
16033  * @cfg {String} minViewMode default empty, (months|years)
16034  * @cfg {Number} startDate default -Infinity
16035  * @cfg {Number} endDate default Infinity
16036  * @cfg {Boolean} todayHighlight default false
16037  * @cfg {Boolean} todayBtn default false
16038  * @cfg {Boolean} calendarWeeks default false
16039  * @cfg {Object} daysOfWeekDisabled default empty
16040  * @cfg {Boolean} singleMode default false (true | false)
16041  * 
16042  * @cfg {Boolean} keyboardNavigation default true
16043  * @cfg {String} language default en
16044  * 
16045  * @constructor
16046  * Create a new DateField
16047  * @param {Object} config The config object
16048  */
16049
16050 Roo.bootstrap.DateField = function(config){
16051     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16052      this.addEvents({
16053             /**
16054              * @event show
16055              * Fires when this field show.
16056              * @param {Roo.bootstrap.DateField} this
16057              * @param {Mixed} date The date value
16058              */
16059             show : true,
16060             /**
16061              * @event show
16062              * Fires when this field hide.
16063              * @param {Roo.bootstrap.DateField} this
16064              * @param {Mixed} date The date value
16065              */
16066             hide : true,
16067             /**
16068              * @event select
16069              * Fires when select a date.
16070              * @param {Roo.bootstrap.DateField} this
16071              * @param {Mixed} date The date value
16072              */
16073             select : true
16074         });
16075 };
16076
16077 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16078     
16079     /**
16080      * @cfg {String} format
16081      * The default date format string which can be overriden for localization support.  The format must be
16082      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16083      */
16084     format : "m/d/y",
16085     /**
16086      * @cfg {String} altFormats
16087      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16088      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16089      */
16090     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16091     
16092     weekStart : 0,
16093     
16094     viewMode : '',
16095     
16096     minViewMode : '',
16097     
16098     todayHighlight : false,
16099     
16100     todayBtn: false,
16101     
16102     language: 'en',
16103     
16104     keyboardNavigation: true,
16105     
16106     calendarWeeks: false,
16107     
16108     startDate: -Infinity,
16109     
16110     endDate: Infinity,
16111     
16112     daysOfWeekDisabled: [],
16113     
16114     _events: [],
16115     
16116     singleMode : false,
16117     
16118     UTCDate: function()
16119     {
16120         return new Date(Date.UTC.apply(Date, arguments));
16121     },
16122     
16123     UTCToday: function()
16124     {
16125         var today = new Date();
16126         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16127     },
16128     
16129     getDate: function() {
16130             var d = this.getUTCDate();
16131             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16132     },
16133     
16134     getUTCDate: function() {
16135             return this.date;
16136     },
16137     
16138     setDate: function(d) {
16139             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16140     },
16141     
16142     setUTCDate: function(d) {
16143             this.date = d;
16144             this.setValue(this.formatDate(this.date));
16145     },
16146         
16147     onRender: function(ct, position)
16148     {
16149         
16150         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16151         
16152         this.language = this.language || 'en';
16153         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16154         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16155         
16156         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16157         this.format = this.format || 'm/d/y';
16158         this.isInline = false;
16159         this.isInput = true;
16160         this.component = this.el.select('.add-on', true).first() || false;
16161         this.component = (this.component && this.component.length === 0) ? false : this.component;
16162         this.hasInput = this.component && this.inputEL().length;
16163         
16164         if (typeof(this.minViewMode === 'string')) {
16165             switch (this.minViewMode) {
16166                 case 'months':
16167                     this.minViewMode = 1;
16168                     break;
16169                 case 'years':
16170                     this.minViewMode = 2;
16171                     break;
16172                 default:
16173                     this.minViewMode = 0;
16174                     break;
16175             }
16176         }
16177         
16178         if (typeof(this.viewMode === 'string')) {
16179             switch (this.viewMode) {
16180                 case 'months':
16181                     this.viewMode = 1;
16182                     break;
16183                 case 'years':
16184                     this.viewMode = 2;
16185                     break;
16186                 default:
16187                     this.viewMode = 0;
16188                     break;
16189             }
16190         }
16191                 
16192         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16193         
16194 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16195         
16196         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16197         
16198         this.picker().on('mousedown', this.onMousedown, this);
16199         this.picker().on('click', this.onClick, this);
16200         
16201         this.picker().addClass('datepicker-dropdown');
16202         
16203         this.startViewMode = this.viewMode;
16204         
16205         if(this.singleMode){
16206             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16207                 v.setVisibilityMode(Roo.Element.DISPLAY)
16208                 v.hide();
16209             });
16210             
16211             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16212                 v.setStyle('width', '189px');
16213             });
16214         }
16215         
16216         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16217             if(!this.calendarWeeks){
16218                 v.remove();
16219                 return;
16220             }
16221             
16222             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16223             v.attr('colspan', function(i, val){
16224                 return parseInt(val) + 1;
16225             });
16226         })
16227                         
16228         
16229         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16230         
16231         this.setStartDate(this.startDate);
16232         this.setEndDate(this.endDate);
16233         
16234         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16235         
16236         this.fillDow();
16237         this.fillMonths();
16238         this.update();
16239         this.showMode();
16240         
16241         if(this.isInline) {
16242             this.show();
16243         }
16244     },
16245     
16246     picker : function()
16247     {
16248         return this.pickerEl;
16249 //        return this.el.select('.datepicker', true).first();
16250     },
16251     
16252     fillDow: function()
16253     {
16254         var dowCnt = this.weekStart;
16255         
16256         var dow = {
16257             tag: 'tr',
16258             cn: [
16259                 
16260             ]
16261         };
16262         
16263         if(this.calendarWeeks){
16264             dow.cn.push({
16265                 tag: 'th',
16266                 cls: 'cw',
16267                 html: '&nbsp;'
16268             })
16269         }
16270         
16271         while (dowCnt < this.weekStart + 7) {
16272             dow.cn.push({
16273                 tag: 'th',
16274                 cls: 'dow',
16275                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16276             });
16277         }
16278         
16279         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16280     },
16281     
16282     fillMonths: function()
16283     {    
16284         var i = 0;
16285         var months = this.picker().select('>.datepicker-months td', true).first();
16286         
16287         months.dom.innerHTML = '';
16288         
16289         while (i < 12) {
16290             var month = {
16291                 tag: 'span',
16292                 cls: 'month',
16293                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16294             }
16295             
16296             months.createChild(month);
16297         }
16298         
16299     },
16300     
16301     update: function()
16302     {
16303         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;
16304         
16305         if (this.date < this.startDate) {
16306             this.viewDate = new Date(this.startDate);
16307         } else if (this.date > this.endDate) {
16308             this.viewDate = new Date(this.endDate);
16309         } else {
16310             this.viewDate = new Date(this.date);
16311         }
16312         
16313         this.fill();
16314     },
16315     
16316     fill: function() 
16317     {
16318         var d = new Date(this.viewDate),
16319                 year = d.getUTCFullYear(),
16320                 month = d.getUTCMonth(),
16321                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16322                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16323                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16324                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16325                 currentDate = this.date && this.date.valueOf(),
16326                 today = this.UTCToday();
16327         
16328         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16329         
16330 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16331         
16332 //        this.picker.select('>tfoot th.today').
16333 //                                              .text(dates[this.language].today)
16334 //                                              .toggle(this.todayBtn !== false);
16335     
16336         this.updateNavArrows();
16337         this.fillMonths();
16338                                                 
16339         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16340         
16341         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16342          
16343         prevMonth.setUTCDate(day);
16344         
16345         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16346         
16347         var nextMonth = new Date(prevMonth);
16348         
16349         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16350         
16351         nextMonth = nextMonth.valueOf();
16352         
16353         var fillMonths = false;
16354         
16355         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16356         
16357         while(prevMonth.valueOf() < nextMonth) {
16358             var clsName = '';
16359             
16360             if (prevMonth.getUTCDay() === this.weekStart) {
16361                 if(fillMonths){
16362                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16363                 }
16364                     
16365                 fillMonths = {
16366                     tag: 'tr',
16367                     cn: []
16368                 };
16369                 
16370                 if(this.calendarWeeks){
16371                     // ISO 8601: First week contains first thursday.
16372                     // ISO also states week starts on Monday, but we can be more abstract here.
16373                     var
16374                     // Start of current week: based on weekstart/current date
16375                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16376                     // Thursday of this week
16377                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16378                     // First Thursday of year, year from thursday
16379                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16380                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16381                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16382                     
16383                     fillMonths.cn.push({
16384                         tag: 'td',
16385                         cls: 'cw',
16386                         html: calWeek
16387                     });
16388                 }
16389             }
16390             
16391             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16392                 clsName += ' old';
16393             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16394                 clsName += ' new';
16395             }
16396             if (this.todayHighlight &&
16397                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16398                 prevMonth.getUTCMonth() == today.getMonth() &&
16399                 prevMonth.getUTCDate() == today.getDate()) {
16400                 clsName += ' today';
16401             }
16402             
16403             if (currentDate && prevMonth.valueOf() === currentDate) {
16404                 clsName += ' active';
16405             }
16406             
16407             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16408                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16409                     clsName += ' disabled';
16410             }
16411             
16412             fillMonths.cn.push({
16413                 tag: 'td',
16414                 cls: 'day ' + clsName,
16415                 html: prevMonth.getDate()
16416             })
16417             
16418             prevMonth.setDate(prevMonth.getDate()+1);
16419         }
16420           
16421         var currentYear = this.date && this.date.getUTCFullYear();
16422         var currentMonth = this.date && this.date.getUTCMonth();
16423         
16424         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16425         
16426         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16427             v.removeClass('active');
16428             
16429             if(currentYear === year && k === currentMonth){
16430                 v.addClass('active');
16431             }
16432             
16433             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16434                 v.addClass('disabled');
16435             }
16436             
16437         });
16438         
16439         
16440         year = parseInt(year/10, 10) * 10;
16441         
16442         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16443         
16444         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16445         
16446         year -= 1;
16447         for (var i = -1; i < 11; i++) {
16448             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16449                 tag: 'span',
16450                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16451                 html: year
16452             })
16453             
16454             year += 1;
16455         }
16456     },
16457     
16458     showMode: function(dir) 
16459     {
16460         if (dir) {
16461             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16462         }
16463         
16464         Roo.each(this.picker().select('>div',true).elements, function(v){
16465             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16466             v.hide();
16467         });
16468         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16469     },
16470     
16471     place: function()
16472     {
16473         if(this.isInline) return;
16474         
16475         this.picker().removeClass(['bottom', 'top']);
16476         
16477         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16478             /*
16479              * place to the top of element!
16480              *
16481              */
16482             
16483             this.picker().addClass('top');
16484             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16485             
16486             return;
16487         }
16488         
16489         this.picker().addClass('bottom');
16490         
16491         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16492     },
16493     
16494     parseDate : function(value)
16495     {
16496         if(!value || value instanceof Date){
16497             return value;
16498         }
16499         var v = Date.parseDate(value, this.format);
16500         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16501             v = Date.parseDate(value, 'Y-m-d');
16502         }
16503         if(!v && this.altFormats){
16504             if(!this.altFormatsArray){
16505                 this.altFormatsArray = this.altFormats.split("|");
16506             }
16507             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16508                 v = Date.parseDate(value, this.altFormatsArray[i]);
16509             }
16510         }
16511         return v;
16512     },
16513     
16514     formatDate : function(date, fmt)
16515     {   
16516         return (!date || !(date instanceof Date)) ?
16517         date : date.dateFormat(fmt || this.format);
16518     },
16519     
16520     onFocus : function()
16521     {
16522         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16523         this.show();
16524     },
16525     
16526     onBlur : function()
16527     {
16528         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16529         
16530         var d = this.inputEl().getValue();
16531         
16532         this.setValue(d);
16533                 
16534         this.hide();
16535     },
16536     
16537     show : function()
16538     {
16539         this.picker().show();
16540         this.update();
16541         this.place();
16542         
16543         this.fireEvent('show', this, this.date);
16544     },
16545     
16546     hide : function()
16547     {
16548         if(this.isInline) return;
16549         this.picker().hide();
16550         this.viewMode = this.startViewMode;
16551         this.showMode();
16552         
16553         this.fireEvent('hide', this, this.date);
16554         
16555     },
16556     
16557     onMousedown: function(e)
16558     {
16559         e.stopPropagation();
16560         e.preventDefault();
16561     },
16562     
16563     keyup: function(e)
16564     {
16565         Roo.bootstrap.DateField.superclass.keyup.call(this);
16566         this.update();
16567     },
16568
16569     setValue: function(v)
16570     {
16571         
16572         // v can be a string or a date..
16573         
16574         
16575         var d = new Date(this.parseDate(v) ).clearTime();
16576         
16577         if(isNaN(d.getTime())){
16578             this.date = this.viewDate = '';
16579             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16580             return;
16581         }
16582         
16583         v = this.formatDate(d);
16584         
16585         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16586         
16587         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16588      
16589         this.update();
16590
16591         this.fireEvent('select', this, this.date);
16592         
16593     },
16594     
16595     getValue: function()
16596     {
16597         return this.formatDate(this.date);
16598     },
16599     
16600     fireKey: function(e)
16601     {
16602         if (!this.picker().isVisible()){
16603             if (e.keyCode == 27) // allow escape to hide and re-show picker
16604                 this.show();
16605             return;
16606         }
16607         
16608         var dateChanged = false,
16609         dir, day, month,
16610         newDate, newViewDate;
16611         
16612         switch(e.keyCode){
16613             case 27: // escape
16614                 this.hide();
16615                 e.preventDefault();
16616                 break;
16617             case 37: // left
16618             case 39: // right
16619                 if (!this.keyboardNavigation) break;
16620                 dir = e.keyCode == 37 ? -1 : 1;
16621                 
16622                 if (e.ctrlKey){
16623                     newDate = this.moveYear(this.date, dir);
16624                     newViewDate = this.moveYear(this.viewDate, dir);
16625                 } else if (e.shiftKey){
16626                     newDate = this.moveMonth(this.date, dir);
16627                     newViewDate = this.moveMonth(this.viewDate, dir);
16628                 } else {
16629                     newDate = new Date(this.date);
16630                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16631                     newViewDate = new Date(this.viewDate);
16632                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16633                 }
16634                 if (this.dateWithinRange(newDate)){
16635                     this.date = newDate;
16636                     this.viewDate = newViewDate;
16637                     this.setValue(this.formatDate(this.date));
16638 //                    this.update();
16639                     e.preventDefault();
16640                     dateChanged = true;
16641                 }
16642                 break;
16643             case 38: // up
16644             case 40: // down
16645                 if (!this.keyboardNavigation) break;
16646                 dir = e.keyCode == 38 ? -1 : 1;
16647                 if (e.ctrlKey){
16648                     newDate = this.moveYear(this.date, dir);
16649                     newViewDate = this.moveYear(this.viewDate, dir);
16650                 } else if (e.shiftKey){
16651                     newDate = this.moveMonth(this.date, dir);
16652                     newViewDate = this.moveMonth(this.viewDate, dir);
16653                 } else {
16654                     newDate = new Date(this.date);
16655                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16656                     newViewDate = new Date(this.viewDate);
16657                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16658                 }
16659                 if (this.dateWithinRange(newDate)){
16660                     this.date = newDate;
16661                     this.viewDate = newViewDate;
16662                     this.setValue(this.formatDate(this.date));
16663 //                    this.update();
16664                     e.preventDefault();
16665                     dateChanged = true;
16666                 }
16667                 break;
16668             case 13: // enter
16669                 this.setValue(this.formatDate(this.date));
16670                 this.hide();
16671                 e.preventDefault();
16672                 break;
16673             case 9: // tab
16674                 this.setValue(this.formatDate(this.date));
16675                 this.hide();
16676                 break;
16677             case 16: // shift
16678             case 17: // ctrl
16679             case 18: // alt
16680                 break;
16681             default :
16682                 this.hide();
16683                 
16684         }
16685     },
16686     
16687     
16688     onClick: function(e) 
16689     {
16690         e.stopPropagation();
16691         e.preventDefault();
16692         
16693         var target = e.getTarget();
16694         
16695         if(target.nodeName.toLowerCase() === 'i'){
16696             target = Roo.get(target).dom.parentNode;
16697         }
16698         
16699         var nodeName = target.nodeName;
16700         var className = target.className;
16701         var html = target.innerHTML;
16702         //Roo.log(nodeName);
16703         
16704         switch(nodeName.toLowerCase()) {
16705             case 'th':
16706                 switch(className) {
16707                     case 'switch':
16708                         this.showMode(1);
16709                         break;
16710                     case 'prev':
16711                     case 'next':
16712                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16713                         switch(this.viewMode){
16714                                 case 0:
16715                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16716                                         break;
16717                                 case 1:
16718                                 case 2:
16719                                         this.viewDate = this.moveYear(this.viewDate, dir);
16720                                         break;
16721                         }
16722                         this.fill();
16723                         break;
16724                     case 'today':
16725                         var date = new Date();
16726                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16727 //                        this.fill()
16728                         this.setValue(this.formatDate(this.date));
16729                         
16730                         this.hide();
16731                         break;
16732                 }
16733                 break;
16734             case 'span':
16735                 if (className.indexOf('disabled') < 0) {
16736                     this.viewDate.setUTCDate(1);
16737                     if (className.indexOf('month') > -1) {
16738                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16739                     } else {
16740                         var year = parseInt(html, 10) || 0;
16741                         this.viewDate.setUTCFullYear(year);
16742                         
16743                     }
16744                     
16745                     if(this.singleMode){
16746                         this.setValue(this.formatDate(this.viewDate));
16747                         this.hide();
16748                         return;
16749                     }
16750                     
16751                     this.showMode(-1);
16752                     this.fill();
16753                 }
16754                 break;
16755                 
16756             case 'td':
16757                 //Roo.log(className);
16758                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16759                     var day = parseInt(html, 10) || 1;
16760                     var year = this.viewDate.getUTCFullYear(),
16761                         month = this.viewDate.getUTCMonth();
16762
16763                     if (className.indexOf('old') > -1) {
16764                         if(month === 0 ){
16765                             month = 11;
16766                             year -= 1;
16767                         }else{
16768                             month -= 1;
16769                         }
16770                     } else if (className.indexOf('new') > -1) {
16771                         if (month == 11) {
16772                             month = 0;
16773                             year += 1;
16774                         } else {
16775                             month += 1;
16776                         }
16777                     }
16778                     //Roo.log([year,month,day]);
16779                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16780                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16781 //                    this.fill();
16782                     //Roo.log(this.formatDate(this.date));
16783                     this.setValue(this.formatDate(this.date));
16784                     this.hide();
16785                 }
16786                 break;
16787         }
16788     },
16789     
16790     setStartDate: function(startDate)
16791     {
16792         this.startDate = startDate || -Infinity;
16793         if (this.startDate !== -Infinity) {
16794             this.startDate = this.parseDate(this.startDate);
16795         }
16796         this.update();
16797         this.updateNavArrows();
16798     },
16799
16800     setEndDate: function(endDate)
16801     {
16802         this.endDate = endDate || Infinity;
16803         if (this.endDate !== Infinity) {
16804             this.endDate = this.parseDate(this.endDate);
16805         }
16806         this.update();
16807         this.updateNavArrows();
16808     },
16809     
16810     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16811     {
16812         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16813         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16814             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16815         }
16816         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16817             return parseInt(d, 10);
16818         });
16819         this.update();
16820         this.updateNavArrows();
16821     },
16822     
16823     updateNavArrows: function() 
16824     {
16825         if(this.singleMode){
16826             return;
16827         }
16828         
16829         var d = new Date(this.viewDate),
16830         year = d.getUTCFullYear(),
16831         month = d.getUTCMonth();
16832         
16833         Roo.each(this.picker().select('.prev', true).elements, function(v){
16834             v.show();
16835             switch (this.viewMode) {
16836                 case 0:
16837
16838                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16839                         v.hide();
16840                     }
16841                     break;
16842                 case 1:
16843                 case 2:
16844                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16845                         v.hide();
16846                     }
16847                     break;
16848             }
16849         });
16850         
16851         Roo.each(this.picker().select('.next', true).elements, function(v){
16852             v.show();
16853             switch (this.viewMode) {
16854                 case 0:
16855
16856                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16857                         v.hide();
16858                     }
16859                     break;
16860                 case 1:
16861                 case 2:
16862                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16863                         v.hide();
16864                     }
16865                     break;
16866             }
16867         })
16868     },
16869     
16870     moveMonth: function(date, dir)
16871     {
16872         if (!dir) return date;
16873         var new_date = new Date(date.valueOf()),
16874         day = new_date.getUTCDate(),
16875         month = new_date.getUTCMonth(),
16876         mag = Math.abs(dir),
16877         new_month, test;
16878         dir = dir > 0 ? 1 : -1;
16879         if (mag == 1){
16880             test = dir == -1
16881             // If going back one month, make sure month is not current month
16882             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16883             ? function(){
16884                 return new_date.getUTCMonth() == month;
16885             }
16886             // If going forward one month, make sure month is as expected
16887             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16888             : function(){
16889                 return new_date.getUTCMonth() != new_month;
16890             };
16891             new_month = month + dir;
16892             new_date.setUTCMonth(new_month);
16893             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16894             if (new_month < 0 || new_month > 11)
16895                 new_month = (new_month + 12) % 12;
16896         } else {
16897             // For magnitudes >1, move one month at a time...
16898             for (var i=0; i<mag; i++)
16899                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16900                 new_date = this.moveMonth(new_date, dir);
16901             // ...then reset the day, keeping it in the new month
16902             new_month = new_date.getUTCMonth();
16903             new_date.setUTCDate(day);
16904             test = function(){
16905                 return new_month != new_date.getUTCMonth();
16906             };
16907         }
16908         // Common date-resetting loop -- if date is beyond end of month, make it
16909         // end of month
16910         while (test()){
16911             new_date.setUTCDate(--day);
16912             new_date.setUTCMonth(new_month);
16913         }
16914         return new_date;
16915     },
16916
16917     moveYear: function(date, dir)
16918     {
16919         return this.moveMonth(date, dir*12);
16920     },
16921
16922     dateWithinRange: function(date)
16923     {
16924         return date >= this.startDate && date <= this.endDate;
16925     },
16926
16927     
16928     remove: function() 
16929     {
16930         this.picker().remove();
16931     }
16932    
16933 });
16934
16935 Roo.apply(Roo.bootstrap.DateField,  {
16936     
16937     head : {
16938         tag: 'thead',
16939         cn: [
16940         {
16941             tag: 'tr',
16942             cn: [
16943             {
16944                 tag: 'th',
16945                 cls: 'prev',
16946                 html: '<i class="fa fa-arrow-left"/>'
16947             },
16948             {
16949                 tag: 'th',
16950                 cls: 'switch',
16951                 colspan: '5'
16952             },
16953             {
16954                 tag: 'th',
16955                 cls: 'next',
16956                 html: '<i class="fa fa-arrow-right"/>'
16957             }
16958
16959             ]
16960         }
16961         ]
16962     },
16963     
16964     content : {
16965         tag: 'tbody',
16966         cn: [
16967         {
16968             tag: 'tr',
16969             cn: [
16970             {
16971                 tag: 'td',
16972                 colspan: '7'
16973             }
16974             ]
16975         }
16976         ]
16977     },
16978     
16979     footer : {
16980         tag: 'tfoot',
16981         cn: [
16982         {
16983             tag: 'tr',
16984             cn: [
16985             {
16986                 tag: 'th',
16987                 colspan: '7',
16988                 cls: 'today'
16989             }
16990                     
16991             ]
16992         }
16993         ]
16994     },
16995     
16996     dates:{
16997         en: {
16998             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16999             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17000             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17001             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17002             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17003             today: "Today"
17004         }
17005     },
17006     
17007     modes: [
17008     {
17009         clsName: 'days',
17010         navFnc: 'Month',
17011         navStep: 1
17012     },
17013     {
17014         clsName: 'months',
17015         navFnc: 'FullYear',
17016         navStep: 1
17017     },
17018     {
17019         clsName: 'years',
17020         navFnc: 'FullYear',
17021         navStep: 10
17022     }]
17023 });
17024
17025 Roo.apply(Roo.bootstrap.DateField,  {
17026   
17027     template : {
17028         tag: 'div',
17029         cls: 'datepicker dropdown-menu roo-dynamic',
17030         cn: [
17031         {
17032             tag: 'div',
17033             cls: 'datepicker-days',
17034             cn: [
17035             {
17036                 tag: 'table',
17037                 cls: 'table-condensed',
17038                 cn:[
17039                 Roo.bootstrap.DateField.head,
17040                 {
17041                     tag: 'tbody'
17042                 },
17043                 Roo.bootstrap.DateField.footer
17044                 ]
17045             }
17046             ]
17047         },
17048         {
17049             tag: 'div',
17050             cls: 'datepicker-months',
17051             cn: [
17052             {
17053                 tag: 'table',
17054                 cls: 'table-condensed',
17055                 cn:[
17056                 Roo.bootstrap.DateField.head,
17057                 Roo.bootstrap.DateField.content,
17058                 Roo.bootstrap.DateField.footer
17059                 ]
17060             }
17061             ]
17062         },
17063         {
17064             tag: 'div',
17065             cls: 'datepicker-years',
17066             cn: [
17067             {
17068                 tag: 'table',
17069                 cls: 'table-condensed',
17070                 cn:[
17071                 Roo.bootstrap.DateField.head,
17072                 Roo.bootstrap.DateField.content,
17073                 Roo.bootstrap.DateField.footer
17074                 ]
17075             }
17076             ]
17077         }
17078         ]
17079     }
17080 });
17081
17082  
17083
17084  /*
17085  * - LGPL
17086  *
17087  * TimeField
17088  * 
17089  */
17090
17091 /**
17092  * @class Roo.bootstrap.TimeField
17093  * @extends Roo.bootstrap.Input
17094  * Bootstrap DateField class
17095  * 
17096  * 
17097  * @constructor
17098  * Create a new TimeField
17099  * @param {Object} config The config object
17100  */
17101
17102 Roo.bootstrap.TimeField = function(config){
17103     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17104     this.addEvents({
17105             /**
17106              * @event show
17107              * Fires when this field show.
17108              * @param {Roo.bootstrap.DateField} thisthis
17109              * @param {Mixed} date The date value
17110              */
17111             show : true,
17112             /**
17113              * @event show
17114              * Fires when this field hide.
17115              * @param {Roo.bootstrap.DateField} this
17116              * @param {Mixed} date The date value
17117              */
17118             hide : true,
17119             /**
17120              * @event select
17121              * Fires when select a date.
17122              * @param {Roo.bootstrap.DateField} this
17123              * @param {Mixed} date The date value
17124              */
17125             select : true
17126         });
17127 };
17128
17129 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17130     
17131     /**
17132      * @cfg {String} format
17133      * The default time format string which can be overriden for localization support.  The format must be
17134      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17135      */
17136     format : "H:i",
17137        
17138     onRender: function(ct, position)
17139     {
17140         
17141         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17142                 
17143         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17144         
17145         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17146         
17147         this.pop = this.picker().select('>.datepicker-time',true).first();
17148         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17149         
17150         this.picker().on('mousedown', this.onMousedown, this);
17151         this.picker().on('click', this.onClick, this);
17152         
17153         this.picker().addClass('datepicker-dropdown');
17154     
17155         this.fillTime();
17156         this.update();
17157             
17158         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17159         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17160         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17161         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17162         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17163         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17164
17165     },
17166     
17167     fireKey: function(e){
17168         if (!this.picker().isVisible()){
17169             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17170                 this.show();
17171             }
17172             return;
17173         }
17174
17175         e.preventDefault();
17176         
17177         switch(e.keyCode){
17178             case 27: // escape
17179                 this.hide();
17180                 break;
17181             case 37: // left
17182             case 39: // right
17183                 this.onTogglePeriod();
17184                 break;
17185             case 38: // up
17186                 this.onIncrementMinutes();
17187                 break;
17188             case 40: // down
17189                 this.onDecrementMinutes();
17190                 break;
17191             case 13: // enter
17192             case 9: // tab
17193                 this.setTime();
17194                 break;
17195         }
17196     },
17197     
17198     onClick: function(e) {
17199         e.stopPropagation();
17200         e.preventDefault();
17201     },
17202     
17203     picker : function()
17204     {
17205         return this.el.select('.datepicker', true).first();
17206     },
17207     
17208     fillTime: function()
17209     {    
17210         var time = this.pop.select('tbody', true).first();
17211         
17212         time.dom.innerHTML = '';
17213         
17214         time.createChild({
17215             tag: 'tr',
17216             cn: [
17217                 {
17218                     tag: 'td',
17219                     cn: [
17220                         {
17221                             tag: 'a',
17222                             href: '#',
17223                             cls: 'btn',
17224                             cn: [
17225                                 {
17226                                     tag: 'span',
17227                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17228                                 }
17229                             ]
17230                         } 
17231                     ]
17232                 },
17233                 {
17234                     tag: 'td',
17235                     cls: 'separator'
17236                 },
17237                 {
17238                     tag: 'td',
17239                     cn: [
17240                         {
17241                             tag: 'a',
17242                             href: '#',
17243                             cls: 'btn',
17244                             cn: [
17245                                 {
17246                                     tag: 'span',
17247                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17248                                 }
17249                             ]
17250                         }
17251                     ]
17252                 },
17253                 {
17254                     tag: 'td',
17255                     cls: 'separator'
17256                 }
17257             ]
17258         });
17259         
17260         time.createChild({
17261             tag: 'tr',
17262             cn: [
17263                 {
17264                     tag: 'td',
17265                     cn: [
17266                         {
17267                             tag: 'span',
17268                             cls: 'timepicker-hour',
17269                             html: '00'
17270                         }  
17271                     ]
17272                 },
17273                 {
17274                     tag: 'td',
17275                     cls: 'separator',
17276                     html: ':'
17277                 },
17278                 {
17279                     tag: 'td',
17280                     cn: [
17281                         {
17282                             tag: 'span',
17283                             cls: 'timepicker-minute',
17284                             html: '00'
17285                         }  
17286                     ]
17287                 },
17288                 {
17289                     tag: 'td',
17290                     cls: 'separator'
17291                 },
17292                 {
17293                     tag: 'td',
17294                     cn: [
17295                         {
17296                             tag: 'button',
17297                             type: 'button',
17298                             cls: 'btn btn-primary period',
17299                             html: 'AM'
17300                             
17301                         }
17302                     ]
17303                 }
17304             ]
17305         });
17306         
17307         time.createChild({
17308             tag: 'tr',
17309             cn: [
17310                 {
17311                     tag: 'td',
17312                     cn: [
17313                         {
17314                             tag: 'a',
17315                             href: '#',
17316                             cls: 'btn',
17317                             cn: [
17318                                 {
17319                                     tag: 'span',
17320                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17321                                 }
17322                             ]
17323                         }
17324                     ]
17325                 },
17326                 {
17327                     tag: 'td',
17328                     cls: 'separator'
17329                 },
17330                 {
17331                     tag: 'td',
17332                     cn: [
17333                         {
17334                             tag: 'a',
17335                             href: '#',
17336                             cls: 'btn',
17337                             cn: [
17338                                 {
17339                                     tag: 'span',
17340                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17341                                 }
17342                             ]
17343                         }
17344                     ]
17345                 },
17346                 {
17347                     tag: 'td',
17348                     cls: 'separator'
17349                 }
17350             ]
17351         });
17352         
17353     },
17354     
17355     update: function()
17356     {
17357         
17358         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17359         
17360         this.fill();
17361     },
17362     
17363     fill: function() 
17364     {
17365         var hours = this.time.getHours();
17366         var minutes = this.time.getMinutes();
17367         var period = 'AM';
17368         
17369         if(hours > 11){
17370             period = 'PM';
17371         }
17372         
17373         if(hours == 0){
17374             hours = 12;
17375         }
17376         
17377         
17378         if(hours > 12){
17379             hours = hours - 12;
17380         }
17381         
17382         if(hours < 10){
17383             hours = '0' + hours;
17384         }
17385         
17386         if(minutes < 10){
17387             minutes = '0' + minutes;
17388         }
17389         
17390         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17391         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17392         this.pop.select('button', true).first().dom.innerHTML = period;
17393         
17394     },
17395     
17396     place: function()
17397     {   
17398         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17399         
17400         var cls = ['bottom'];
17401         
17402         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17403             cls.pop();
17404             cls.push('top');
17405         }
17406         
17407         cls.push('right');
17408         
17409         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17410             cls.pop();
17411             cls.push('left');
17412         }
17413         
17414         this.picker().addClass(cls.join('-'));
17415         
17416         var _this = this;
17417         
17418         Roo.each(cls, function(c){
17419             if(c == 'bottom'){
17420                 _this.picker().setTop(_this.inputEl().getHeight());
17421                 return;
17422             }
17423             if(c == 'top'){
17424                 _this.picker().setTop(0 - _this.picker().getHeight());
17425                 return;
17426             }
17427             
17428             if(c == 'left'){
17429                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17430                 return;
17431             }
17432             if(c == 'right'){
17433                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17434                 return;
17435             }
17436         });
17437         
17438     },
17439   
17440     onFocus : function()
17441     {
17442         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17443         this.show();
17444     },
17445     
17446     onBlur : function()
17447     {
17448         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17449         this.hide();
17450     },
17451     
17452     show : function()
17453     {
17454         this.picker().show();
17455         this.pop.show();
17456         this.update();
17457         this.place();
17458         
17459         this.fireEvent('show', this, this.date);
17460     },
17461     
17462     hide : function()
17463     {
17464         this.picker().hide();
17465         this.pop.hide();
17466         
17467         this.fireEvent('hide', this, this.date);
17468     },
17469     
17470     setTime : function()
17471     {
17472         this.hide();
17473         this.setValue(this.time.format(this.format));
17474         
17475         this.fireEvent('select', this, this.date);
17476         
17477         
17478     },
17479     
17480     onMousedown: function(e){
17481         e.stopPropagation();
17482         e.preventDefault();
17483     },
17484     
17485     onIncrementHours: function()
17486     {
17487         Roo.log('onIncrementHours');
17488         this.time = this.time.add(Date.HOUR, 1);
17489         this.update();
17490         
17491     },
17492     
17493     onDecrementHours: function()
17494     {
17495         Roo.log('onDecrementHours');
17496         this.time = this.time.add(Date.HOUR, -1);
17497         this.update();
17498     },
17499     
17500     onIncrementMinutes: function()
17501     {
17502         Roo.log('onIncrementMinutes');
17503         this.time = this.time.add(Date.MINUTE, 1);
17504         this.update();
17505     },
17506     
17507     onDecrementMinutes: function()
17508     {
17509         Roo.log('onDecrementMinutes');
17510         this.time = this.time.add(Date.MINUTE, -1);
17511         this.update();
17512     },
17513     
17514     onTogglePeriod: function()
17515     {
17516         Roo.log('onTogglePeriod');
17517         this.time = this.time.add(Date.HOUR, 12);
17518         this.update();
17519     }
17520     
17521    
17522 });
17523
17524 Roo.apply(Roo.bootstrap.TimeField,  {
17525     
17526     content : {
17527         tag: 'tbody',
17528         cn: [
17529             {
17530                 tag: 'tr',
17531                 cn: [
17532                 {
17533                     tag: 'td',
17534                     colspan: '7'
17535                 }
17536                 ]
17537             }
17538         ]
17539     },
17540     
17541     footer : {
17542         tag: 'tfoot',
17543         cn: [
17544             {
17545                 tag: 'tr',
17546                 cn: [
17547                 {
17548                     tag: 'th',
17549                     colspan: '7',
17550                     cls: '',
17551                     cn: [
17552                         {
17553                             tag: 'button',
17554                             cls: 'btn btn-info ok',
17555                             html: 'OK'
17556                         }
17557                     ]
17558                 }
17559
17560                 ]
17561             }
17562         ]
17563     }
17564 });
17565
17566 Roo.apply(Roo.bootstrap.TimeField,  {
17567   
17568     template : {
17569         tag: 'div',
17570         cls: 'datepicker dropdown-menu',
17571         cn: [
17572             {
17573                 tag: 'div',
17574                 cls: 'datepicker-time',
17575                 cn: [
17576                 {
17577                     tag: 'table',
17578                     cls: 'table-condensed',
17579                     cn:[
17580                     Roo.bootstrap.TimeField.content,
17581                     Roo.bootstrap.TimeField.footer
17582                     ]
17583                 }
17584                 ]
17585             }
17586         ]
17587     }
17588 });
17589
17590  
17591
17592  /*
17593  * - LGPL
17594  *
17595  * MonthField
17596  * 
17597  */
17598
17599 /**
17600  * @class Roo.bootstrap.MonthField
17601  * @extends Roo.bootstrap.Input
17602  * Bootstrap MonthField class
17603  * 
17604  * @cfg {String} language default en
17605  * 
17606  * @constructor
17607  * Create a new MonthField
17608  * @param {Object} config The config object
17609  */
17610
17611 Roo.bootstrap.MonthField = function(config){
17612     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17613     
17614     this.addEvents({
17615         /**
17616          * @event show
17617          * Fires when this field show.
17618          * @param {Roo.bootstrap.MonthField} this
17619          * @param {Mixed} date The date value
17620          */
17621         show : true,
17622         /**
17623          * @event show
17624          * Fires when this field hide.
17625          * @param {Roo.bootstrap.MonthField} this
17626          * @param {Mixed} date The date value
17627          */
17628         hide : true,
17629         /**
17630          * @event select
17631          * Fires when select a date.
17632          * @param {Roo.bootstrap.MonthField} this
17633          * @param {String} oldvalue The old value
17634          * @param {String} newvalue The new value
17635          */
17636         select : true
17637     });
17638 };
17639
17640 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17641     
17642     onRender: function(ct, position)
17643     {
17644         
17645         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17646         
17647         this.language = this.language || 'en';
17648         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17649         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17650         
17651         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17652         this.isInline = false;
17653         this.isInput = true;
17654         this.component = this.el.select('.add-on', true).first() || false;
17655         this.component = (this.component && this.component.length === 0) ? false : this.component;
17656         this.hasInput = this.component && this.inputEL().length;
17657         
17658         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17659         
17660         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17661         
17662         this.picker().on('mousedown', this.onMousedown, this);
17663         this.picker().on('click', this.onClick, this);
17664         
17665         this.picker().addClass('datepicker-dropdown');
17666         
17667         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17668             v.setStyle('width', '189px');
17669         });
17670         
17671         this.fillMonths();
17672         
17673         this.update();
17674         
17675         if(this.isInline) {
17676             this.show();
17677         }
17678         
17679     },
17680     
17681     setValue: function(v, suppressEvent)
17682     {   
17683         var o = this.getValue();
17684         
17685         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17686         
17687         this.update();
17688
17689         if(suppressEvent !== true){
17690             this.fireEvent('select', this, o, v);
17691         }
17692         
17693     },
17694     
17695     getValue: function()
17696     {
17697         return this.value;
17698     },
17699     
17700     onClick: function(e) 
17701     {
17702         e.stopPropagation();
17703         e.preventDefault();
17704         
17705         var target = e.getTarget();
17706         
17707         if(target.nodeName.toLowerCase() === 'i'){
17708             target = Roo.get(target).dom.parentNode;
17709         }
17710         
17711         var nodeName = target.nodeName;
17712         var className = target.className;
17713         var html = target.innerHTML;
17714         
17715         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17716             return;
17717         }
17718         
17719         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17720         
17721         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17722         
17723         this.hide();
17724                         
17725     },
17726     
17727     picker : function()
17728     {
17729         return this.pickerEl;
17730     },
17731     
17732     fillMonths: function()
17733     {    
17734         var i = 0;
17735         var months = this.picker().select('>.datepicker-months td', true).first();
17736         
17737         months.dom.innerHTML = '';
17738         
17739         while (i < 12) {
17740             var month = {
17741                 tag: 'span',
17742                 cls: 'month',
17743                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17744             }
17745             
17746             months.createChild(month);
17747         }
17748         
17749     },
17750     
17751     update: function()
17752     {
17753         var _this = this;
17754         
17755         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17756             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17757         }
17758         
17759         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17760             e.removeClass('active');
17761             
17762             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17763                 e.addClass('active');
17764             }
17765         })
17766     },
17767     
17768     place: function()
17769     {
17770         if(this.isInline) return;
17771         
17772         this.picker().removeClass(['bottom', 'top']);
17773         
17774         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17775             /*
17776              * place to the top of element!
17777              *
17778              */
17779             
17780             this.picker().addClass('top');
17781             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17782             
17783             return;
17784         }
17785         
17786         this.picker().addClass('bottom');
17787         
17788         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17789     },
17790     
17791     onFocus : function()
17792     {
17793         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17794         this.show();
17795     },
17796     
17797     onBlur : function()
17798     {
17799         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17800         
17801         var d = this.inputEl().getValue();
17802         
17803         this.setValue(d);
17804                 
17805         this.hide();
17806     },
17807     
17808     show : function()
17809     {
17810         this.picker().show();
17811         this.picker().select('>.datepicker-months', true).first().show();
17812         this.update();
17813         this.place();
17814         
17815         this.fireEvent('show', this, this.date);
17816     },
17817     
17818     hide : function()
17819     {
17820         if(this.isInline) return;
17821         this.picker().hide();
17822         this.fireEvent('hide', this, this.date);
17823         
17824     },
17825     
17826     onMousedown: function(e)
17827     {
17828         e.stopPropagation();
17829         e.preventDefault();
17830     },
17831     
17832     keyup: function(e)
17833     {
17834         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17835         this.update();
17836     },
17837
17838     fireKey: function(e)
17839     {
17840         if (!this.picker().isVisible()){
17841             if (e.keyCode == 27) // allow escape to hide and re-show picker
17842                 this.show();
17843             return;
17844         }
17845         
17846         var dir;
17847         
17848         switch(e.keyCode){
17849             case 27: // escape
17850                 this.hide();
17851                 e.preventDefault();
17852                 break;
17853             case 37: // left
17854             case 39: // right
17855                 dir = e.keyCode == 37 ? -1 : 1;
17856                 
17857                 this.vIndex = this.vIndex + dir;
17858                 
17859                 if(this.vIndex < 0){
17860                     this.vIndex = 0;
17861                 }
17862                 
17863                 if(this.vIndex > 11){
17864                     this.vIndex = 11;
17865                 }
17866                 
17867                 if(isNaN(this.vIndex)){
17868                     this.vIndex = 0;
17869                 }
17870                 
17871                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17872                 
17873                 break;
17874             case 38: // up
17875             case 40: // down
17876                 
17877                 dir = e.keyCode == 38 ? -1 : 1;
17878                 
17879                 this.vIndex = this.vIndex + dir * 4;
17880                 
17881                 if(this.vIndex < 0){
17882                     this.vIndex = 0;
17883                 }
17884                 
17885                 if(this.vIndex > 11){
17886                     this.vIndex = 11;
17887                 }
17888                 
17889                 if(isNaN(this.vIndex)){
17890                     this.vIndex = 0;
17891                 }
17892                 
17893                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17894                 break;
17895                 
17896             case 13: // enter
17897                 
17898                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17899                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17900                 }
17901                 
17902                 this.hide();
17903                 e.preventDefault();
17904                 break;
17905             case 9: // tab
17906                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17907                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17908                 }
17909                 this.hide();
17910                 break;
17911             case 16: // shift
17912             case 17: // ctrl
17913             case 18: // alt
17914                 break;
17915             default :
17916                 this.hide();
17917                 
17918         }
17919     },
17920     
17921     remove: function() 
17922     {
17923         this.picker().remove();
17924     }
17925    
17926 });
17927
17928 Roo.apply(Roo.bootstrap.MonthField,  {
17929     
17930     content : {
17931         tag: 'tbody',
17932         cn: [
17933         {
17934             tag: 'tr',
17935             cn: [
17936             {
17937                 tag: 'td',
17938                 colspan: '7'
17939             }
17940             ]
17941         }
17942         ]
17943     },
17944     
17945     dates:{
17946         en: {
17947             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17948             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17949         }
17950     }
17951 });
17952
17953 Roo.apply(Roo.bootstrap.MonthField,  {
17954   
17955     template : {
17956         tag: 'div',
17957         cls: 'datepicker dropdown-menu roo-dynamic',
17958         cn: [
17959             {
17960                 tag: 'div',
17961                 cls: 'datepicker-months',
17962                 cn: [
17963                 {
17964                     tag: 'table',
17965                     cls: 'table-condensed',
17966                     cn:[
17967                         Roo.bootstrap.DateField.content
17968                     ]
17969                 }
17970                 ]
17971             }
17972         ]
17973     }
17974 });
17975
17976  
17977
17978  
17979  /*
17980  * - LGPL
17981  *
17982  * CheckBox
17983  * 
17984  */
17985
17986 /**
17987  * @class Roo.bootstrap.CheckBox
17988  * @extends Roo.bootstrap.Input
17989  * Bootstrap CheckBox class
17990  * 
17991  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17992  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17993  * @cfg {String} boxLabel The text that appears beside the checkbox
17994  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17995  * @cfg {Boolean} checked initnal the element
17996  * @cfg {Boolean} inline inline the element (default false)
17997  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17998  * 
17999  * @constructor
18000  * Create a new CheckBox
18001  * @param {Object} config The config object
18002  */
18003
18004 Roo.bootstrap.CheckBox = function(config){
18005     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18006    
18007     this.addEvents({
18008         /**
18009         * @event check
18010         * Fires when the element is checked or unchecked.
18011         * @param {Roo.bootstrap.CheckBox} this This input
18012         * @param {Boolean} checked The new checked value
18013         */
18014        check : true
18015     });
18016     
18017 };
18018
18019 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18020   
18021     inputType: 'checkbox',
18022     inputValue: 1,
18023     valueOff: 0,
18024     boxLabel: false,
18025     checked: false,
18026     weight : false,
18027     inline: false,
18028     
18029     getAutoCreate : function()
18030     {
18031         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18032         
18033         var id = Roo.id();
18034         
18035         var cfg = {};
18036         
18037         cfg.cls = 'form-group ' + this.inputType; //input-group
18038         
18039         if(this.inline){
18040             cfg.cls += ' ' + this.inputType + '-inline';
18041         }
18042         
18043         var input =  {
18044             tag: 'input',
18045             id : id,
18046             type : this.inputType,
18047             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18048             cls : 'roo-' + this.inputType, //'form-box',
18049             placeholder : this.placeholder || ''
18050             
18051         };
18052         
18053         if (this.weight) { // Validity check?
18054             cfg.cls += " " + this.inputType + "-" + this.weight;
18055         }
18056         
18057         if (this.disabled) {
18058             input.disabled=true;
18059         }
18060         
18061         if(this.checked){
18062             input.checked = this.checked;
18063         }
18064         
18065         if (this.name) {
18066             input.name = this.name;
18067         }
18068         
18069         if (this.size) {
18070             input.cls += ' input-' + this.size;
18071         }
18072         
18073         var settings=this;
18074         
18075         ['xs','sm','md','lg'].map(function(size){
18076             if (settings[size]) {
18077                 cfg.cls += ' col-' + size + '-' + settings[size];
18078             }
18079         });
18080         
18081         var inputblock = input;
18082          
18083         if (this.before || this.after) {
18084             
18085             inputblock = {
18086                 cls : 'input-group',
18087                 cn :  [] 
18088             };
18089             
18090             if (this.before) {
18091                 inputblock.cn.push({
18092                     tag :'span',
18093                     cls : 'input-group-addon',
18094                     html : this.before
18095                 });
18096             }
18097             
18098             inputblock.cn.push(input);
18099             
18100             if (this.after) {
18101                 inputblock.cn.push({
18102                     tag :'span',
18103                     cls : 'input-group-addon',
18104                     html : this.after
18105                 });
18106             }
18107             
18108         }
18109         
18110         if (align ==='left' && this.fieldLabel.length) {
18111                 Roo.log("left and has label");
18112                 cfg.cn = [
18113                     
18114                     {
18115                         tag: 'label',
18116                         'for' :  id,
18117                         cls : 'control-label col-md-' + this.labelWidth,
18118                         html : this.fieldLabel
18119                         
18120                     },
18121                     {
18122                         cls : "col-md-" + (12 - this.labelWidth), 
18123                         cn: [
18124                             inputblock
18125                         ]
18126                     }
18127                     
18128                 ];
18129         } else if ( this.fieldLabel.length) {
18130                 Roo.log(" label");
18131                 cfg.cn = [
18132                    
18133                     {
18134                         tag: this.boxLabel ? 'span' : 'label',
18135                         'for': id,
18136                         cls: 'control-label box-input-label',
18137                         //cls : 'input-group-addon',
18138                         html : this.fieldLabel
18139                         
18140                     },
18141                     
18142                     inputblock
18143                     
18144                 ];
18145
18146         } else {
18147             
18148                 Roo.log(" no label && no align");
18149                 cfg.cn = [  inputblock ] ;
18150                 
18151                 
18152         }
18153         if(this.boxLabel){
18154              var boxLabelCfg = {
18155                 tag: 'label',
18156                 //'for': id, // box label is handled by onclick - so no for...
18157                 cls: 'box-label',
18158                 html: this.boxLabel
18159             }
18160             
18161             if(this.tooltip){
18162                 boxLabelCfg.tooltip = this.tooltip;
18163             }
18164              
18165             cfg.cn.push(boxLabelCfg);
18166         }
18167         
18168         
18169        
18170         return cfg;
18171         
18172     },
18173     
18174     /**
18175      * return the real input element.
18176      */
18177     inputEl: function ()
18178     {
18179         return this.el.select('input.roo-' + this.inputType,true).first();
18180     },
18181     
18182     labelEl: function()
18183     {
18184         return this.el.select('label.control-label',true).first();
18185     },
18186     /* depricated... */
18187     
18188     label: function()
18189     {
18190         return this.labelEl();
18191     },
18192     
18193     initEvents : function()
18194     {
18195 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18196         
18197         this.inputEl().on('click', this.onClick,  this);
18198         
18199         if (this.boxLabel) { 
18200             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18201         }
18202         
18203         this.startValue = this.getValue();
18204         
18205         if(this.groupId){
18206             Roo.bootstrap.CheckBox.register(this);
18207         }
18208     },
18209     
18210     onClick : function()
18211     {   
18212         this.setChecked(!this.checked);
18213     },
18214     
18215     setChecked : function(state,suppressEvent)
18216     {
18217         this.startValue = this.getValue();
18218         
18219         if(this.inputType == 'radio'){
18220             
18221             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18222                 e.dom.checked = false;
18223             });
18224             
18225             this.inputEl().dom.checked = true;
18226             
18227             this.inputEl().dom.value = this.inputValue;
18228             
18229             if(suppressEvent !== true){
18230                 this.fireEvent('check', this, true);
18231             }
18232             
18233             this.validate();
18234             
18235             return;
18236         }
18237         
18238         this.checked = state;
18239         
18240         this.inputEl().dom.checked = state;
18241         
18242         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18243         
18244         if(suppressEvent !== true){
18245             this.fireEvent('check', this, state);
18246         }
18247         
18248         this.validate();
18249     },
18250     
18251     getValue : function()
18252     {
18253         if(this.inputType == 'radio'){
18254             return this.getGroupValue();
18255         }
18256         
18257         return this.inputEl().getValue();
18258         
18259     },
18260     
18261     getGroupValue : function()
18262     {
18263         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18264             return '';
18265         }
18266         
18267         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18268     },
18269     
18270     setValue : function(v,suppressEvent)
18271     {
18272         if(this.inputType == 'radio'){
18273             this.setGroupValue(v, suppressEvent);
18274             return;
18275         }
18276         
18277         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18278         
18279         this.validate();
18280     },
18281     
18282     setGroupValue : function(v, suppressEvent)
18283     {
18284         this.startValue = this.getValue();
18285         
18286         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18287             e.dom.checked = false;
18288             
18289             if(e.dom.value == v){
18290                 e.dom.checked = true;
18291             }
18292         });
18293         
18294         if(suppressEvent !== true){
18295             this.fireEvent('check', this, true);
18296         }
18297
18298         this.validate();
18299         
18300         return;
18301     },
18302     
18303     validate : function()
18304     {
18305         if(
18306                 this.disabled || 
18307                 (this.inputType == 'radio' && this.validateRadio()) ||
18308                 (this.inputType == 'checkbox' && this.validateCheckbox())
18309         ){
18310             this.markValid();
18311             return true;
18312         }
18313         
18314         this.markInvalid();
18315         return false;
18316     },
18317     
18318     validateRadio : function()
18319     {
18320         var valid = false;
18321         
18322         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18323             if(!e.dom.checked){
18324                 return;
18325             }
18326             
18327             valid = true;
18328             
18329             return false;
18330         });
18331         
18332         return valid;
18333     },
18334     
18335     validateCheckbox : function()
18336     {
18337         if(!this.groupId){
18338             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18339         }
18340         
18341         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18342         
18343         if(!group){
18344             return false;
18345         }
18346         
18347         var r = false;
18348         
18349         for(var i in group){
18350             if(r){
18351                 break;
18352             }
18353             
18354             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18355         }
18356         
18357         return r;
18358     },
18359     
18360     /**
18361      * Mark this field as valid
18362      */
18363     markValid : function()
18364     {
18365         if(this.allowBlank){
18366             return;
18367         }
18368         
18369         var _this = this;
18370         
18371         this.fireEvent('valid', this);
18372         
18373         if(this.inputType == 'radio'){
18374             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18375                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18376                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18377             });
18378             
18379             return;
18380         }
18381         
18382         if(!this.groupId){
18383             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18384             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18385             return;
18386         }
18387         
18388         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18389             
18390         if(!group){
18391             return;
18392         }
18393         
18394         for(var i in group){
18395             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18396             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18397         }
18398     },
18399     
18400      /**
18401      * Mark this field as invalid
18402      * @param {String} msg The validation message
18403      */
18404     markInvalid : function(msg)
18405     {
18406         if(this.allowBlank){
18407             return;
18408         }
18409         
18410         var _this = this;
18411         
18412         this.fireEvent('invalid', this, msg);
18413         
18414         if(this.inputType == 'radio'){
18415             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18416                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18417                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18418             });
18419             
18420             return;
18421         }
18422         
18423         if(!this.groupId){
18424             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18425             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18426             return;
18427         }
18428         
18429         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18430             
18431         if(!group){
18432             return;
18433         }
18434         
18435         for(var i in group){
18436             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18437             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18438         }
18439         
18440     }
18441     
18442 });
18443
18444 Roo.apply(Roo.bootstrap.CheckBox, {
18445     
18446     groups: {},
18447     
18448      /**
18449     * register a CheckBox Group
18450     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18451     */
18452     register : function(checkbox)
18453     {
18454         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18455             this.groups[checkbox.groupId] = {};
18456         }
18457         
18458         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18459             return;
18460         }
18461         
18462         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18463         
18464     },
18465     /**
18466     * fetch a CheckBox Group based on the group ID
18467     * @param {string} the group ID
18468     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18469     */
18470     get: function(groupId) {
18471         if (typeof(this.groups[groupId]) == 'undefined') {
18472             return false;
18473         }
18474         
18475         return this.groups[groupId] ;
18476     }
18477     
18478     
18479 });
18480 /*
18481  * - LGPL
18482  *
18483  * Radio
18484  *
18485  *
18486  * not inline
18487  *<div class="radio">
18488   <label>
18489     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18490     Option one is this and that&mdash;be sure to include why it's great
18491   </label>
18492 </div>
18493  *
18494  *
18495  *inline
18496  *<span>
18497  *<label class="radio-inline">fieldLabel</label>
18498  *<label class="radio-inline">
18499   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18500 </label>
18501 <span>
18502  * 
18503  * 
18504  */
18505
18506 /**
18507  * @class Roo.bootstrap.Radio
18508  * @extends Roo.bootstrap.CheckBox
18509  * Bootstrap Radio class
18510
18511  * @constructor
18512  * Create a new Radio
18513  * @param {Object} config The config object
18514  */
18515
18516 Roo.bootstrap.Radio = function(config){
18517     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18518    
18519 };
18520
18521 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18522     
18523     inputType: 'radio',
18524     inputValue: '',
18525     valueOff: '',
18526     
18527     getAutoCreate : function()
18528     {
18529         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18530         align = align || 'left'; // default...
18531         
18532         
18533         
18534         var id = Roo.id();
18535         
18536         var cfg = {
18537                 tag : this.inline ? 'span' : 'div',
18538                 cls : '',
18539                 cn : []
18540         };
18541         
18542         var inline = this.inline ? ' radio-inline' : '';
18543         
18544         var lbl = {
18545                 tag: 'label' ,
18546                 // does not need for, as we wrap the input with it..
18547                 'for' : id,
18548                 cls : 'control-label box-label' + inline,
18549                 cn : []
18550         };
18551         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18552         
18553         var fieldLabel = {
18554             tag: 'label' ,
18555             //cls : 'control-label' + inline,
18556             html : this.fieldLabel,
18557             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18558         };
18559         
18560  
18561         
18562         
18563         var input =  {
18564             tag: 'input',
18565             id : id,
18566             type : this.inputType,
18567             //value : (!this.checked) ? this.valueOff : this.inputValue,
18568             value : this.inputValue,
18569             cls : 'roo-radio',
18570             placeholder : this.placeholder || '' // ?? needed????
18571             
18572         };
18573         if (this.weight) { // Validity check?
18574             input.cls += " radio-" + this.weight;
18575         }
18576         if (this.disabled) {
18577             input.disabled=true;
18578         }
18579         
18580         if(this.checked){
18581             input.checked = this.checked;
18582         }
18583         
18584         if (this.name) {
18585             input.name = this.name;
18586         }
18587         
18588         if (this.size) {
18589             input.cls += ' input-' + this.size;
18590         }
18591         
18592         //?? can span's inline have a width??
18593         
18594         var settings=this;
18595         ['xs','sm','md','lg'].map(function(size){
18596             if (settings[size]) {
18597                 cfg.cls += ' col-' + size + '-' + settings[size];
18598             }
18599         });
18600         
18601         var inputblock = input;
18602         
18603         if (this.before || this.after) {
18604             
18605             inputblock = {
18606                 cls : 'input-group',
18607                 tag : 'span',
18608                 cn :  [] 
18609             };
18610             if (this.before) {
18611                 inputblock.cn.push({
18612                     tag :'span',
18613                     cls : 'input-group-addon',
18614                     html : this.before
18615                 });
18616             }
18617             inputblock.cn.push(input);
18618             if (this.after) {
18619                 inputblock.cn.push({
18620                     tag :'span',
18621                     cls : 'input-group-addon',
18622                     html : this.after
18623                 });
18624             }
18625             
18626         };
18627         
18628         
18629         if (this.fieldLabel && this.fieldLabel.length) {
18630             cfg.cn.push(fieldLabel);
18631         }
18632        
18633         // normal bootstrap puts the input inside the label.
18634         // however with our styled version - it has to go after the input.
18635        
18636         //lbl.cn.push(inputblock);
18637         
18638         var lblwrap =  {
18639             tag: 'span',
18640             cls: 'radio' + inline,
18641             cn: [
18642                 inputblock,
18643                 lbl
18644             ]
18645         };
18646         
18647         cfg.cn.push( lblwrap);
18648         
18649         if(this.boxLabel){
18650             lbl.cn.push({
18651                 tag: 'span',
18652                 html: this.boxLabel
18653             })
18654         }
18655          
18656         
18657         return cfg;
18658         
18659     },
18660     
18661     initEvents : function()
18662     {
18663 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18664         
18665         this.inputEl().on('click', this.onClick,  this);
18666         if (this.boxLabel) {
18667             Roo.log('find label')
18668             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18669         }
18670         
18671     },
18672     
18673     inputEl: function ()
18674     {
18675         return this.el.select('input.roo-radio',true).first();
18676     },
18677     onClick : function()
18678     {   
18679         Roo.log("click");
18680         this.setChecked(true);
18681     },
18682     
18683     setChecked : function(state,suppressEvent)
18684     {
18685         if(state){
18686             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18687                 v.dom.checked = false;
18688             });
18689         }
18690         Roo.log(this.inputEl().dom);
18691         this.checked = state;
18692         this.inputEl().dom.checked = state;
18693         
18694         if(suppressEvent !== true){
18695             this.fireEvent('check', this, state);
18696         }
18697         
18698         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18699         
18700     },
18701     
18702     getGroupValue : function()
18703     {
18704         var value = '';
18705         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18706             if(v.dom.checked == true){
18707                 value = v.dom.value;
18708             }
18709         });
18710         
18711         return value;
18712     },
18713     
18714     /**
18715      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18716      * @return {Mixed} value The field value
18717      */
18718     getValue : function(){
18719         return this.getGroupValue();
18720     }
18721     
18722 });
18723
18724  
18725 //<script type="text/javascript">
18726
18727 /*
18728  * Based  Ext JS Library 1.1.1
18729  * Copyright(c) 2006-2007, Ext JS, LLC.
18730  * LGPL
18731  *
18732  */
18733  
18734 /**
18735  * @class Roo.HtmlEditorCore
18736  * @extends Roo.Component
18737  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18738  *
18739  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18740  */
18741
18742 Roo.HtmlEditorCore = function(config){
18743     
18744     
18745     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18746     
18747     
18748     this.addEvents({
18749         /**
18750          * @event initialize
18751          * Fires when the editor is fully initialized (including the iframe)
18752          * @param {Roo.HtmlEditorCore} this
18753          */
18754         initialize: true,
18755         /**
18756          * @event activate
18757          * Fires when the editor is first receives the focus. Any insertion must wait
18758          * until after this event.
18759          * @param {Roo.HtmlEditorCore} this
18760          */
18761         activate: true,
18762          /**
18763          * @event beforesync
18764          * Fires before the textarea is updated with content from the editor iframe. Return false
18765          * to cancel the sync.
18766          * @param {Roo.HtmlEditorCore} this
18767          * @param {String} html
18768          */
18769         beforesync: true,
18770          /**
18771          * @event beforepush
18772          * Fires before the iframe editor is updated with content from the textarea. Return false
18773          * to cancel the push.
18774          * @param {Roo.HtmlEditorCore} this
18775          * @param {String} html
18776          */
18777         beforepush: true,
18778          /**
18779          * @event sync
18780          * Fires when the textarea is updated with content from the editor iframe.
18781          * @param {Roo.HtmlEditorCore} this
18782          * @param {String} html
18783          */
18784         sync: true,
18785          /**
18786          * @event push
18787          * Fires when the iframe editor is updated with content from the textarea.
18788          * @param {Roo.HtmlEditorCore} this
18789          * @param {String} html
18790          */
18791         push: true,
18792         
18793         /**
18794          * @event editorevent
18795          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18796          * @param {Roo.HtmlEditorCore} this
18797          */
18798         editorevent: true
18799         
18800     });
18801     
18802     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18803     
18804     // defaults : white / black...
18805     this.applyBlacklists();
18806     
18807     
18808     
18809 };
18810
18811
18812 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18813
18814
18815      /**
18816      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18817      */
18818     
18819     owner : false,
18820     
18821      /**
18822      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18823      *                        Roo.resizable.
18824      */
18825     resizable : false,
18826      /**
18827      * @cfg {Number} height (in pixels)
18828      */   
18829     height: 300,
18830    /**
18831      * @cfg {Number} width (in pixels)
18832      */   
18833     width: 500,
18834     
18835     /**
18836      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18837      * 
18838      */
18839     stylesheets: false,
18840     
18841     // id of frame..
18842     frameId: false,
18843     
18844     // private properties
18845     validationEvent : false,
18846     deferHeight: true,
18847     initialized : false,
18848     activated : false,
18849     sourceEditMode : false,
18850     onFocus : Roo.emptyFn,
18851     iframePad:3,
18852     hideMode:'offsets',
18853     
18854     clearUp: true,
18855     
18856     // blacklist + whitelisted elements..
18857     black: false,
18858     white: false,
18859      
18860     
18861
18862     /**
18863      * Protected method that will not generally be called directly. It
18864      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18865      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18866      */
18867     getDocMarkup : function(){
18868         // body styles..
18869         var st = '';
18870         
18871         // inherit styels from page...?? 
18872         if (this.stylesheets === false) {
18873             
18874             Roo.get(document.head).select('style').each(function(node) {
18875                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18876             });
18877             
18878             Roo.get(document.head).select('link').each(function(node) { 
18879                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18880             });
18881             
18882         } else if (!this.stylesheets.length) {
18883                 // simple..
18884                 st = '<style type="text/css">' +
18885                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18886                    '</style>';
18887         } else { 
18888             
18889         }
18890         
18891         st +=  '<style type="text/css">' +
18892             'IMG { cursor: pointer } ' +
18893         '</style>';
18894
18895         
18896         return '<html><head>' + st  +
18897             //<style type="text/css">' +
18898             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18899             //'</style>' +
18900             ' </head><body class="roo-htmleditor-body"></body></html>';
18901     },
18902
18903     // private
18904     onRender : function(ct, position)
18905     {
18906         var _t = this;
18907         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
18908         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
18909         
18910         
18911         this.el.dom.style.border = '0 none';
18912         this.el.dom.setAttribute('tabIndex', -1);
18913         this.el.addClass('x-hidden hide');
18914         
18915         
18916         
18917         if(Roo.isIE){ // fix IE 1px bogus margin
18918             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
18919         }
18920        
18921         
18922         this.frameId = Roo.id();
18923         
18924          
18925         
18926         var iframe = this.owner.wrap.createChild({
18927             tag: 'iframe',
18928             cls: 'form-control', // bootstrap..
18929             id: this.frameId,
18930             name: this.frameId,
18931             frameBorder : 'no',
18932             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
18933         }, this.el
18934         );
18935         
18936         
18937         this.iframe = iframe.dom;
18938
18939          this.assignDocWin();
18940         
18941         this.doc.designMode = 'on';
18942        
18943         this.doc.open();
18944         this.doc.write(this.getDocMarkup());
18945         this.doc.close();
18946
18947         
18948         var task = { // must defer to wait for browser to be ready
18949             run : function(){
18950                 //console.log("run task?" + this.doc.readyState);
18951                 this.assignDocWin();
18952                 if(this.doc.body || this.doc.readyState == 'complete'){
18953                     try {
18954                         this.doc.designMode="on";
18955                     } catch (e) {
18956                         return;
18957                     }
18958                     Roo.TaskMgr.stop(task);
18959                     this.initEditor.defer(10, this);
18960                 }
18961             },
18962             interval : 10,
18963             duration: 10000,
18964             scope: this
18965         };
18966         Roo.TaskMgr.start(task);
18967
18968     },
18969
18970     // private
18971     onResize : function(w, h)
18972     {
18973          Roo.log('resize: ' +w + ',' + h );
18974         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18975         if(!this.iframe){
18976             return;
18977         }
18978         if(typeof w == 'number'){
18979             
18980             this.iframe.style.width = w + 'px';
18981         }
18982         if(typeof h == 'number'){
18983             
18984             this.iframe.style.height = h + 'px';
18985             if(this.doc){
18986                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18987             }
18988         }
18989         
18990     },
18991
18992     /**
18993      * Toggles the editor between standard and source edit mode.
18994      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18995      */
18996     toggleSourceEdit : function(sourceEditMode){
18997         
18998         this.sourceEditMode = sourceEditMode === true;
18999         
19000         if(this.sourceEditMode){
19001  
19002             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19003             
19004         }else{
19005             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19006             //this.iframe.className = '';
19007             this.deferFocus();
19008         }
19009         //this.setSize(this.owner.wrap.getSize());
19010         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19011     },
19012
19013     
19014   
19015
19016     /**
19017      * Protected method that will not generally be called directly. If you need/want
19018      * custom HTML cleanup, this is the method you should override.
19019      * @param {String} html The HTML to be cleaned
19020      * return {String} The cleaned HTML
19021      */
19022     cleanHtml : function(html){
19023         html = String(html);
19024         if(html.length > 5){
19025             if(Roo.isSafari){ // strip safari nonsense
19026                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19027             }
19028         }
19029         if(html == '&nbsp;'){
19030             html = '';
19031         }
19032         return html;
19033     },
19034
19035     /**
19036      * HTML Editor -> Textarea
19037      * Protected method that will not generally be called directly. Syncs the contents
19038      * of the editor iframe with the textarea.
19039      */
19040     syncValue : function(){
19041         if(this.initialized){
19042             var bd = (this.doc.body || this.doc.documentElement);
19043             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19044             var html = bd.innerHTML;
19045             if(Roo.isSafari){
19046                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19047                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19048                 if(m && m[1]){
19049                     html = '<div style="'+m[0]+'">' + html + '</div>';
19050                 }
19051             }
19052             html = this.cleanHtml(html);
19053             // fix up the special chars.. normaly like back quotes in word...
19054             // however we do not want to do this with chinese..
19055             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19056                 var cc = b.charCodeAt();
19057                 if (
19058                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19059                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19060                     (cc >= 0xf900 && cc < 0xfb00 )
19061                 ) {
19062                         return b;
19063                 }
19064                 return "&#"+cc+";" 
19065             });
19066             if(this.owner.fireEvent('beforesync', this, html) !== false){
19067                 this.el.dom.value = html;
19068                 this.owner.fireEvent('sync', this, html);
19069             }
19070         }
19071     },
19072
19073     /**
19074      * Protected method that will not generally be called directly. Pushes the value of the textarea
19075      * into the iframe editor.
19076      */
19077     pushValue : function(){
19078         if(this.initialized){
19079             var v = this.el.dom.value.trim();
19080             
19081 //            if(v.length < 1){
19082 //                v = '&#160;';
19083 //            }
19084             
19085             if(this.owner.fireEvent('beforepush', this, v) !== false){
19086                 var d = (this.doc.body || this.doc.documentElement);
19087                 d.innerHTML = v;
19088                 this.cleanUpPaste();
19089                 this.el.dom.value = d.innerHTML;
19090                 this.owner.fireEvent('push', this, v);
19091             }
19092         }
19093     },
19094
19095     // private
19096     deferFocus : function(){
19097         this.focus.defer(10, this);
19098     },
19099
19100     // doc'ed in Field
19101     focus : function(){
19102         if(this.win && !this.sourceEditMode){
19103             this.win.focus();
19104         }else{
19105             this.el.focus();
19106         }
19107     },
19108     
19109     assignDocWin: function()
19110     {
19111         var iframe = this.iframe;
19112         
19113          if(Roo.isIE){
19114             this.doc = iframe.contentWindow.document;
19115             this.win = iframe.contentWindow;
19116         } else {
19117 //            if (!Roo.get(this.frameId)) {
19118 //                return;
19119 //            }
19120 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19121 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19122             
19123             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19124                 return;
19125             }
19126             
19127             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19128             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19129         }
19130     },
19131     
19132     // private
19133     initEditor : function(){
19134         //console.log("INIT EDITOR");
19135         this.assignDocWin();
19136         
19137         
19138         
19139         this.doc.designMode="on";
19140         this.doc.open();
19141         this.doc.write(this.getDocMarkup());
19142         this.doc.close();
19143         
19144         var dbody = (this.doc.body || this.doc.documentElement);
19145         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19146         // this copies styles from the containing element into thsi one..
19147         // not sure why we need all of this..
19148         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19149         
19150         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19151         //ss['background-attachment'] = 'fixed'; // w3c
19152         dbody.bgProperties = 'fixed'; // ie
19153         //Roo.DomHelper.applyStyles(dbody, ss);
19154         Roo.EventManager.on(this.doc, {
19155             //'mousedown': this.onEditorEvent,
19156             'mouseup': this.onEditorEvent,
19157             'dblclick': this.onEditorEvent,
19158             'click': this.onEditorEvent,
19159             'keyup': this.onEditorEvent,
19160             buffer:100,
19161             scope: this
19162         });
19163         if(Roo.isGecko){
19164             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19165         }
19166         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19167             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19168         }
19169         this.initialized = true;
19170
19171         this.owner.fireEvent('initialize', this);
19172         this.pushValue();
19173     },
19174
19175     // private
19176     onDestroy : function(){
19177         
19178         
19179         
19180         if(this.rendered){
19181             
19182             //for (var i =0; i < this.toolbars.length;i++) {
19183             //    // fixme - ask toolbars for heights?
19184             //    this.toolbars[i].onDestroy();
19185            // }
19186             
19187             //this.wrap.dom.innerHTML = '';
19188             //this.wrap.remove();
19189         }
19190     },
19191
19192     // private
19193     onFirstFocus : function(){
19194         
19195         this.assignDocWin();
19196         
19197         
19198         this.activated = true;
19199          
19200     
19201         if(Roo.isGecko){ // prevent silly gecko errors
19202             this.win.focus();
19203             var s = this.win.getSelection();
19204             if(!s.focusNode || s.focusNode.nodeType != 3){
19205                 var r = s.getRangeAt(0);
19206                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19207                 r.collapse(true);
19208                 this.deferFocus();
19209             }
19210             try{
19211                 this.execCmd('useCSS', true);
19212                 this.execCmd('styleWithCSS', false);
19213             }catch(e){}
19214         }
19215         this.owner.fireEvent('activate', this);
19216     },
19217
19218     // private
19219     adjustFont: function(btn){
19220         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19221         //if(Roo.isSafari){ // safari
19222         //    adjust *= 2;
19223        // }
19224         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19225         if(Roo.isSafari){ // safari
19226             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19227             v =  (v < 10) ? 10 : v;
19228             v =  (v > 48) ? 48 : v;
19229             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19230             
19231         }
19232         
19233         
19234         v = Math.max(1, v+adjust);
19235         
19236         this.execCmd('FontSize', v  );
19237     },
19238
19239     onEditorEvent : function(e)
19240     {
19241         this.owner.fireEvent('editorevent', this, e);
19242       //  this.updateToolbar();
19243         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19244     },
19245
19246     insertTag : function(tg)
19247     {
19248         // could be a bit smarter... -> wrap the current selected tRoo..
19249         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19250             
19251             range = this.createRange(this.getSelection());
19252             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19253             wrappingNode.appendChild(range.extractContents());
19254             range.insertNode(wrappingNode);
19255
19256             return;
19257             
19258             
19259             
19260         }
19261         this.execCmd("formatblock",   tg);
19262         
19263     },
19264     
19265     insertText : function(txt)
19266     {
19267         
19268         
19269         var range = this.createRange();
19270         range.deleteContents();
19271                //alert(Sender.getAttribute('label'));
19272                
19273         range.insertNode(this.doc.createTextNode(txt));
19274     } ,
19275     
19276      
19277
19278     /**
19279      * Executes a Midas editor command on the editor document and performs necessary focus and
19280      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19281      * @param {String} cmd The Midas command
19282      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19283      */
19284     relayCmd : function(cmd, value){
19285         this.win.focus();
19286         this.execCmd(cmd, value);
19287         this.owner.fireEvent('editorevent', this);
19288         //this.updateToolbar();
19289         this.owner.deferFocus();
19290     },
19291
19292     /**
19293      * Executes a Midas editor command directly on the editor document.
19294      * For visual commands, you should use {@link #relayCmd} instead.
19295      * <b>This should only be called after the editor is initialized.</b>
19296      * @param {String} cmd The Midas command
19297      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19298      */
19299     execCmd : function(cmd, value){
19300         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19301         this.syncValue();
19302     },
19303  
19304  
19305    
19306     /**
19307      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19308      * to insert tRoo.
19309      * @param {String} text | dom node.. 
19310      */
19311     insertAtCursor : function(text)
19312     {
19313         
19314         
19315         
19316         if(!this.activated){
19317             return;
19318         }
19319         /*
19320         if(Roo.isIE){
19321             this.win.focus();
19322             var r = this.doc.selection.createRange();
19323             if(r){
19324                 r.collapse(true);
19325                 r.pasteHTML(text);
19326                 this.syncValue();
19327                 this.deferFocus();
19328             
19329             }
19330             return;
19331         }
19332         */
19333         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19334             this.win.focus();
19335             
19336             
19337             // from jquery ui (MIT licenced)
19338             var range, node;
19339             var win = this.win;
19340             
19341             if (win.getSelection && win.getSelection().getRangeAt) {
19342                 range = win.getSelection().getRangeAt(0);
19343                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19344                 range.insertNode(node);
19345             } else if (win.document.selection && win.document.selection.createRange) {
19346                 // no firefox support
19347                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19348                 win.document.selection.createRange().pasteHTML(txt);
19349             } else {
19350                 // no firefox support
19351                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19352                 this.execCmd('InsertHTML', txt);
19353             } 
19354             
19355             this.syncValue();
19356             
19357             this.deferFocus();
19358         }
19359     },
19360  // private
19361     mozKeyPress : function(e){
19362         if(e.ctrlKey){
19363             var c = e.getCharCode(), cmd;
19364           
19365             if(c > 0){
19366                 c = String.fromCharCode(c).toLowerCase();
19367                 switch(c){
19368                     case 'b':
19369                         cmd = 'bold';
19370                         break;
19371                     case 'i':
19372                         cmd = 'italic';
19373                         break;
19374                     
19375                     case 'u':
19376                         cmd = 'underline';
19377                         break;
19378                     
19379                     case 'v':
19380                         this.cleanUpPaste.defer(100, this);
19381                         return;
19382                         
19383                 }
19384                 if(cmd){
19385                     this.win.focus();
19386                     this.execCmd(cmd);
19387                     this.deferFocus();
19388                     e.preventDefault();
19389                 }
19390                 
19391             }
19392         }
19393     },
19394
19395     // private
19396     fixKeys : function(){ // load time branching for fastest keydown performance
19397         if(Roo.isIE){
19398             return function(e){
19399                 var k = e.getKey(), r;
19400                 if(k == e.TAB){
19401                     e.stopEvent();
19402                     r = this.doc.selection.createRange();
19403                     if(r){
19404                         r.collapse(true);
19405                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19406                         this.deferFocus();
19407                     }
19408                     return;
19409                 }
19410                 
19411                 if(k == e.ENTER){
19412                     r = this.doc.selection.createRange();
19413                     if(r){
19414                         var target = r.parentElement();
19415                         if(!target || target.tagName.toLowerCase() != 'li'){
19416                             e.stopEvent();
19417                             r.pasteHTML('<br />');
19418                             r.collapse(false);
19419                             r.select();
19420                         }
19421                     }
19422                 }
19423                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19424                     this.cleanUpPaste.defer(100, this);
19425                     return;
19426                 }
19427                 
19428                 
19429             };
19430         }else if(Roo.isOpera){
19431             return function(e){
19432                 var k = e.getKey();
19433                 if(k == e.TAB){
19434                     e.stopEvent();
19435                     this.win.focus();
19436                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19437                     this.deferFocus();
19438                 }
19439                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19440                     this.cleanUpPaste.defer(100, this);
19441                     return;
19442                 }
19443                 
19444             };
19445         }else if(Roo.isSafari){
19446             return function(e){
19447                 var k = e.getKey();
19448                 
19449                 if(k == e.TAB){
19450                     e.stopEvent();
19451                     this.execCmd('InsertText','\t');
19452                     this.deferFocus();
19453                     return;
19454                 }
19455                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19456                     this.cleanUpPaste.defer(100, this);
19457                     return;
19458                 }
19459                 
19460              };
19461         }
19462     }(),
19463     
19464     getAllAncestors: function()
19465     {
19466         var p = this.getSelectedNode();
19467         var a = [];
19468         if (!p) {
19469             a.push(p); // push blank onto stack..
19470             p = this.getParentElement();
19471         }
19472         
19473         
19474         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19475             a.push(p);
19476             p = p.parentNode;
19477         }
19478         a.push(this.doc.body);
19479         return a;
19480     },
19481     lastSel : false,
19482     lastSelNode : false,
19483     
19484     
19485     getSelection : function() 
19486     {
19487         this.assignDocWin();
19488         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19489     },
19490     
19491     getSelectedNode: function() 
19492     {
19493         // this may only work on Gecko!!!
19494         
19495         // should we cache this!!!!
19496         
19497         
19498         
19499          
19500         var range = this.createRange(this.getSelection()).cloneRange();
19501         
19502         if (Roo.isIE) {
19503             var parent = range.parentElement();
19504             while (true) {
19505                 var testRange = range.duplicate();
19506                 testRange.moveToElementText(parent);
19507                 if (testRange.inRange(range)) {
19508                     break;
19509                 }
19510                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19511                     break;
19512                 }
19513                 parent = parent.parentElement;
19514             }
19515             return parent;
19516         }
19517         
19518         // is ancestor a text element.
19519         var ac =  range.commonAncestorContainer;
19520         if (ac.nodeType == 3) {
19521             ac = ac.parentNode;
19522         }
19523         
19524         var ar = ac.childNodes;
19525          
19526         var nodes = [];
19527         var other_nodes = [];
19528         var has_other_nodes = false;
19529         for (var i=0;i<ar.length;i++) {
19530             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19531                 continue;
19532             }
19533             // fullly contained node.
19534             
19535             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19536                 nodes.push(ar[i]);
19537                 continue;
19538             }
19539             
19540             // probably selected..
19541             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19542                 other_nodes.push(ar[i]);
19543                 continue;
19544             }
19545             // outer..
19546             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19547                 continue;
19548             }
19549             
19550             
19551             has_other_nodes = true;
19552         }
19553         if (!nodes.length && other_nodes.length) {
19554             nodes= other_nodes;
19555         }
19556         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19557             return false;
19558         }
19559         
19560         return nodes[0];
19561     },
19562     createRange: function(sel)
19563     {
19564         // this has strange effects when using with 
19565         // top toolbar - not sure if it's a great idea.
19566         //this.editor.contentWindow.focus();
19567         if (typeof sel != "undefined") {
19568             try {
19569                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19570             } catch(e) {
19571                 return this.doc.createRange();
19572             }
19573         } else {
19574             return this.doc.createRange();
19575         }
19576     },
19577     getParentElement: function()
19578     {
19579         
19580         this.assignDocWin();
19581         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19582         
19583         var range = this.createRange(sel);
19584          
19585         try {
19586             var p = range.commonAncestorContainer;
19587             while (p.nodeType == 3) { // text node
19588                 p = p.parentNode;
19589             }
19590             return p;
19591         } catch (e) {
19592             return null;
19593         }
19594     
19595     },
19596     /***
19597      *
19598      * Range intersection.. the hard stuff...
19599      *  '-1' = before
19600      *  '0' = hits..
19601      *  '1' = after.
19602      *         [ -- selected range --- ]
19603      *   [fail]                        [fail]
19604      *
19605      *    basically..
19606      *      if end is before start or  hits it. fail.
19607      *      if start is after end or hits it fail.
19608      *
19609      *   if either hits (but other is outside. - then it's not 
19610      *   
19611      *    
19612      **/
19613     
19614     
19615     // @see http://www.thismuchiknow.co.uk/?p=64.
19616     rangeIntersectsNode : function(range, node)
19617     {
19618         var nodeRange = node.ownerDocument.createRange();
19619         try {
19620             nodeRange.selectNode(node);
19621         } catch (e) {
19622             nodeRange.selectNodeContents(node);
19623         }
19624     
19625         var rangeStartRange = range.cloneRange();
19626         rangeStartRange.collapse(true);
19627     
19628         var rangeEndRange = range.cloneRange();
19629         rangeEndRange.collapse(false);
19630     
19631         var nodeStartRange = nodeRange.cloneRange();
19632         nodeStartRange.collapse(true);
19633     
19634         var nodeEndRange = nodeRange.cloneRange();
19635         nodeEndRange.collapse(false);
19636     
19637         return rangeStartRange.compareBoundaryPoints(
19638                  Range.START_TO_START, nodeEndRange) == -1 &&
19639                rangeEndRange.compareBoundaryPoints(
19640                  Range.START_TO_START, nodeStartRange) == 1;
19641         
19642          
19643     },
19644     rangeCompareNode : function(range, node)
19645     {
19646         var nodeRange = node.ownerDocument.createRange();
19647         try {
19648             nodeRange.selectNode(node);
19649         } catch (e) {
19650             nodeRange.selectNodeContents(node);
19651         }
19652         
19653         
19654         range.collapse(true);
19655     
19656         nodeRange.collapse(true);
19657      
19658         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19659         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19660          
19661         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19662         
19663         var nodeIsBefore   =  ss == 1;
19664         var nodeIsAfter    = ee == -1;
19665         
19666         if (nodeIsBefore && nodeIsAfter)
19667             return 0; // outer
19668         if (!nodeIsBefore && nodeIsAfter)
19669             return 1; //right trailed.
19670         
19671         if (nodeIsBefore && !nodeIsAfter)
19672             return 2;  // left trailed.
19673         // fully contined.
19674         return 3;
19675     },
19676
19677     // private? - in a new class?
19678     cleanUpPaste :  function()
19679     {
19680         // cleans up the whole document..
19681         Roo.log('cleanuppaste');
19682         
19683         this.cleanUpChildren(this.doc.body);
19684         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19685         if (clean != this.doc.body.innerHTML) {
19686             this.doc.body.innerHTML = clean;
19687         }
19688         
19689     },
19690     
19691     cleanWordChars : function(input) {// change the chars to hex code
19692         var he = Roo.HtmlEditorCore;
19693         
19694         var output = input;
19695         Roo.each(he.swapCodes, function(sw) { 
19696             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19697             
19698             output = output.replace(swapper, sw[1]);
19699         });
19700         
19701         return output;
19702     },
19703     
19704     
19705     cleanUpChildren : function (n)
19706     {
19707         if (!n.childNodes.length) {
19708             return;
19709         }
19710         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19711            this.cleanUpChild(n.childNodes[i]);
19712         }
19713     },
19714     
19715     
19716         
19717     
19718     cleanUpChild : function (node)
19719     {
19720         var ed = this;
19721         //console.log(node);
19722         if (node.nodeName == "#text") {
19723             // clean up silly Windows -- stuff?
19724             return; 
19725         }
19726         if (node.nodeName == "#comment") {
19727             node.parentNode.removeChild(node);
19728             // clean up silly Windows -- stuff?
19729             return; 
19730         }
19731         var lcname = node.tagName.toLowerCase();
19732         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19733         // whitelist of tags..
19734         
19735         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19736             // remove node.
19737             node.parentNode.removeChild(node);
19738             return;
19739             
19740         }
19741         
19742         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19743         
19744         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19745         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19746         
19747         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19748         //    remove_keep_children = true;
19749         //}
19750         
19751         if (remove_keep_children) {
19752             this.cleanUpChildren(node);
19753             // inserts everything just before this node...
19754             while (node.childNodes.length) {
19755                 var cn = node.childNodes[0];
19756                 node.removeChild(cn);
19757                 node.parentNode.insertBefore(cn, node);
19758             }
19759             node.parentNode.removeChild(node);
19760             return;
19761         }
19762         
19763         if (!node.attributes || !node.attributes.length) {
19764             this.cleanUpChildren(node);
19765             return;
19766         }
19767         
19768         function cleanAttr(n,v)
19769         {
19770             
19771             if (v.match(/^\./) || v.match(/^\//)) {
19772                 return;
19773             }
19774             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19775                 return;
19776             }
19777             if (v.match(/^#/)) {
19778                 return;
19779             }
19780 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19781             node.removeAttribute(n);
19782             
19783         }
19784         
19785         var cwhite = this.cwhite;
19786         var cblack = this.cblack;
19787             
19788         function cleanStyle(n,v)
19789         {
19790             if (v.match(/expression/)) { //XSS?? should we even bother..
19791                 node.removeAttribute(n);
19792                 return;
19793             }
19794             
19795             var parts = v.split(/;/);
19796             var clean = [];
19797             
19798             Roo.each(parts, function(p) {
19799                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19800                 if (!p.length) {
19801                     return true;
19802                 }
19803                 var l = p.split(':').shift().replace(/\s+/g,'');
19804                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19805                 
19806                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19807 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19808                     //node.removeAttribute(n);
19809                     return true;
19810                 }
19811                 //Roo.log()
19812                 // only allow 'c whitelisted system attributes'
19813                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19814 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19815                     //node.removeAttribute(n);
19816                     return true;
19817                 }
19818                 
19819                 
19820                  
19821                 
19822                 clean.push(p);
19823                 return true;
19824             });
19825             if (clean.length) { 
19826                 node.setAttribute(n, clean.join(';'));
19827             } else {
19828                 node.removeAttribute(n);
19829             }
19830             
19831         }
19832         
19833         
19834         for (var i = node.attributes.length-1; i > -1 ; i--) {
19835             var a = node.attributes[i];
19836             //console.log(a);
19837             
19838             if (a.name.toLowerCase().substr(0,2)=='on')  {
19839                 node.removeAttribute(a.name);
19840                 continue;
19841             }
19842             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19843                 node.removeAttribute(a.name);
19844                 continue;
19845             }
19846             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19847                 cleanAttr(a.name,a.value); // fixme..
19848                 continue;
19849             }
19850             if (a.name == 'style') {
19851                 cleanStyle(a.name,a.value);
19852                 continue;
19853             }
19854             /// clean up MS crap..
19855             // tecnically this should be a list of valid class'es..
19856             
19857             
19858             if (a.name == 'class') {
19859                 if (a.value.match(/^Mso/)) {
19860                     node.className = '';
19861                 }
19862                 
19863                 if (a.value.match(/body/)) {
19864                     node.className = '';
19865                 }
19866                 continue;
19867             }
19868             
19869             // style cleanup!?
19870             // class cleanup?
19871             
19872         }
19873         
19874         
19875         this.cleanUpChildren(node);
19876         
19877         
19878     },
19879     
19880     /**
19881      * Clean up MS wordisms...
19882      */
19883     cleanWord : function(node)
19884     {
19885         
19886         
19887         if (!node) {
19888             this.cleanWord(this.doc.body);
19889             return;
19890         }
19891         if (node.nodeName == "#text") {
19892             // clean up silly Windows -- stuff?
19893             return; 
19894         }
19895         if (node.nodeName == "#comment") {
19896             node.parentNode.removeChild(node);
19897             // clean up silly Windows -- stuff?
19898             return; 
19899         }
19900         
19901         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19902             node.parentNode.removeChild(node);
19903             return;
19904         }
19905         
19906         // remove - but keep children..
19907         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
19908             while (node.childNodes.length) {
19909                 var cn = node.childNodes[0];
19910                 node.removeChild(cn);
19911                 node.parentNode.insertBefore(cn, node);
19912             }
19913             node.parentNode.removeChild(node);
19914             this.iterateChildren(node, this.cleanWord);
19915             return;
19916         }
19917         // clean styles
19918         if (node.className.length) {
19919             
19920             var cn = node.className.split(/\W+/);
19921             var cna = [];
19922             Roo.each(cn, function(cls) {
19923                 if (cls.match(/Mso[a-zA-Z]+/)) {
19924                     return;
19925                 }
19926                 cna.push(cls);
19927             });
19928             node.className = cna.length ? cna.join(' ') : '';
19929             if (!cna.length) {
19930                 node.removeAttribute("class");
19931             }
19932         }
19933         
19934         if (node.hasAttribute("lang")) {
19935             node.removeAttribute("lang");
19936         }
19937         
19938         if (node.hasAttribute("style")) {
19939             
19940             var styles = node.getAttribute("style").split(";");
19941             var nstyle = [];
19942             Roo.each(styles, function(s) {
19943                 if (!s.match(/:/)) {
19944                     return;
19945                 }
19946                 var kv = s.split(":");
19947                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19948                     return;
19949                 }
19950                 // what ever is left... we allow.
19951                 nstyle.push(s);
19952             });
19953             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19954             if (!nstyle.length) {
19955                 node.removeAttribute('style');
19956             }
19957         }
19958         this.iterateChildren(node, this.cleanWord);
19959         
19960         
19961         
19962     },
19963     /**
19964      * iterateChildren of a Node, calling fn each time, using this as the scole..
19965      * @param {DomNode} node node to iterate children of.
19966      * @param {Function} fn method of this class to call on each item.
19967      */
19968     iterateChildren : function(node, fn)
19969     {
19970         if (!node.childNodes.length) {
19971                 return;
19972         }
19973         for (var i = node.childNodes.length-1; i > -1 ; i--) {
19974            fn.call(this, node.childNodes[i])
19975         }
19976     },
19977     
19978     
19979     /**
19980      * cleanTableWidths.
19981      *
19982      * Quite often pasting from word etc.. results in tables with column and widths.
19983      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
19984      *
19985      */
19986     cleanTableWidths : function(node)
19987     {
19988          
19989          
19990         if (!node) {
19991             this.cleanTableWidths(this.doc.body);
19992             return;
19993         }
19994         
19995         // ignore list...
19996         if (node.nodeName == "#text" || node.nodeName == "#comment") {
19997             return; 
19998         }
19999         Roo.log(node.tagName);
20000         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20001             this.iterateChildren(node, this.cleanTableWidths);
20002             return;
20003         }
20004         if (node.hasAttribute('width')) {
20005             node.removeAttribute('width');
20006         }
20007         
20008          
20009         if (node.hasAttribute("style")) {
20010             // pretty basic...
20011             
20012             var styles = node.getAttribute("style").split(";");
20013             var nstyle = [];
20014             Roo.each(styles, function(s) {
20015                 if (!s.match(/:/)) {
20016                     return;
20017                 }
20018                 var kv = s.split(":");
20019                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20020                     return;
20021                 }
20022                 // what ever is left... we allow.
20023                 nstyle.push(s);
20024             });
20025             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20026             if (!nstyle.length) {
20027                 node.removeAttribute('style');
20028             }
20029         }
20030         
20031         this.iterateChildren(node, this.cleanTableWidths);
20032         
20033         
20034     },
20035     
20036     
20037     
20038     
20039     domToHTML : function(currentElement, depth, nopadtext) {
20040         
20041         depth = depth || 0;
20042         nopadtext = nopadtext || false;
20043     
20044         if (!currentElement) {
20045             return this.domToHTML(this.doc.body);
20046         }
20047         
20048         //Roo.log(currentElement);
20049         var j;
20050         var allText = false;
20051         var nodeName = currentElement.nodeName;
20052         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20053         
20054         if  (nodeName == '#text') {
20055             
20056             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20057         }
20058         
20059         
20060         var ret = '';
20061         if (nodeName != 'BODY') {
20062              
20063             var i = 0;
20064             // Prints the node tagName, such as <A>, <IMG>, etc
20065             if (tagName) {
20066                 var attr = [];
20067                 for(i = 0; i < currentElement.attributes.length;i++) {
20068                     // quoting?
20069                     var aname = currentElement.attributes.item(i).name;
20070                     if (!currentElement.attributes.item(i).value.length) {
20071                         continue;
20072                     }
20073                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20074                 }
20075                 
20076                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20077             } 
20078             else {
20079                 
20080                 // eack
20081             }
20082         } else {
20083             tagName = false;
20084         }
20085         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20086             return ret;
20087         }
20088         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20089             nopadtext = true;
20090         }
20091         
20092         
20093         // Traverse the tree
20094         i = 0;
20095         var currentElementChild = currentElement.childNodes.item(i);
20096         var allText = true;
20097         var innerHTML  = '';
20098         lastnode = '';
20099         while (currentElementChild) {
20100             // Formatting code (indent the tree so it looks nice on the screen)
20101             var nopad = nopadtext;
20102             if (lastnode == 'SPAN') {
20103                 nopad  = true;
20104             }
20105             // text
20106             if  (currentElementChild.nodeName == '#text') {
20107                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20108                 toadd = nopadtext ? toadd : toadd.trim();
20109                 if (!nopad && toadd.length > 80) {
20110                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20111                 }
20112                 innerHTML  += toadd;
20113                 
20114                 i++;
20115                 currentElementChild = currentElement.childNodes.item(i);
20116                 lastNode = '';
20117                 continue;
20118             }
20119             allText = false;
20120             
20121             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20122                 
20123             // Recursively traverse the tree structure of the child node
20124             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20125             lastnode = currentElementChild.nodeName;
20126             i++;
20127             currentElementChild=currentElement.childNodes.item(i);
20128         }
20129         
20130         ret += innerHTML;
20131         
20132         if (!allText) {
20133                 // The remaining code is mostly for formatting the tree
20134             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20135         }
20136         
20137         
20138         if (tagName) {
20139             ret+= "</"+tagName+">";
20140         }
20141         return ret;
20142         
20143     },
20144         
20145     applyBlacklists : function()
20146     {
20147         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20148         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20149         
20150         this.white = [];
20151         this.black = [];
20152         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20153             if (b.indexOf(tag) > -1) {
20154                 return;
20155             }
20156             this.white.push(tag);
20157             
20158         }, this);
20159         
20160         Roo.each(w, function(tag) {
20161             if (b.indexOf(tag) > -1) {
20162                 return;
20163             }
20164             if (this.white.indexOf(tag) > -1) {
20165                 return;
20166             }
20167             this.white.push(tag);
20168             
20169         }, this);
20170         
20171         
20172         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20173             if (w.indexOf(tag) > -1) {
20174                 return;
20175             }
20176             this.black.push(tag);
20177             
20178         }, this);
20179         
20180         Roo.each(b, function(tag) {
20181             if (w.indexOf(tag) > -1) {
20182                 return;
20183             }
20184             if (this.black.indexOf(tag) > -1) {
20185                 return;
20186             }
20187             this.black.push(tag);
20188             
20189         }, this);
20190         
20191         
20192         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20193         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20194         
20195         this.cwhite = [];
20196         this.cblack = [];
20197         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20198             if (b.indexOf(tag) > -1) {
20199                 return;
20200             }
20201             this.cwhite.push(tag);
20202             
20203         }, this);
20204         
20205         Roo.each(w, function(tag) {
20206             if (b.indexOf(tag) > -1) {
20207                 return;
20208             }
20209             if (this.cwhite.indexOf(tag) > -1) {
20210                 return;
20211             }
20212             this.cwhite.push(tag);
20213             
20214         }, this);
20215         
20216         
20217         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20218             if (w.indexOf(tag) > -1) {
20219                 return;
20220             }
20221             this.cblack.push(tag);
20222             
20223         }, this);
20224         
20225         Roo.each(b, function(tag) {
20226             if (w.indexOf(tag) > -1) {
20227                 return;
20228             }
20229             if (this.cblack.indexOf(tag) > -1) {
20230                 return;
20231             }
20232             this.cblack.push(tag);
20233             
20234         }, this);
20235     },
20236     
20237     setStylesheets : function(stylesheets)
20238     {
20239         if(typeof(stylesheets) == 'string'){
20240             Roo.get(this.iframe.contentDocument.head).createChild({
20241                 tag : 'link',
20242                 rel : 'stylesheet',
20243                 type : 'text/css',
20244                 href : stylesheets
20245             });
20246             
20247             return;
20248         }
20249         var _this = this;
20250      
20251         Roo.each(stylesheets, function(s) {
20252             if(!s.length){
20253                 return;
20254             }
20255             
20256             Roo.get(_this.iframe.contentDocument.head).createChild({
20257                 tag : 'link',
20258                 rel : 'stylesheet',
20259                 type : 'text/css',
20260                 href : s
20261             });
20262         });
20263
20264         
20265     },
20266     
20267     removeStylesheets : function()
20268     {
20269         var _this = this;
20270         
20271         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20272             s.remove();
20273         });
20274     }
20275     
20276     // hide stuff that is not compatible
20277     /**
20278      * @event blur
20279      * @hide
20280      */
20281     /**
20282      * @event change
20283      * @hide
20284      */
20285     /**
20286      * @event focus
20287      * @hide
20288      */
20289     /**
20290      * @event specialkey
20291      * @hide
20292      */
20293     /**
20294      * @cfg {String} fieldClass @hide
20295      */
20296     /**
20297      * @cfg {String} focusClass @hide
20298      */
20299     /**
20300      * @cfg {String} autoCreate @hide
20301      */
20302     /**
20303      * @cfg {String} inputType @hide
20304      */
20305     /**
20306      * @cfg {String} invalidClass @hide
20307      */
20308     /**
20309      * @cfg {String} invalidText @hide
20310      */
20311     /**
20312      * @cfg {String} msgFx @hide
20313      */
20314     /**
20315      * @cfg {String} validateOnBlur @hide
20316      */
20317 });
20318
20319 Roo.HtmlEditorCore.white = [
20320         'area', 'br', 'img', 'input', 'hr', 'wbr',
20321         
20322        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20323        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20324        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20325        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20326        'table',   'ul',         'xmp', 
20327        
20328        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20329       'thead',   'tr', 
20330      
20331       'dir', 'menu', 'ol', 'ul', 'dl',
20332        
20333       'embed',  'object'
20334 ];
20335
20336
20337 Roo.HtmlEditorCore.black = [
20338     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20339         'applet', // 
20340         'base',   'basefont', 'bgsound', 'blink',  'body', 
20341         'frame',  'frameset', 'head',    'html',   'ilayer', 
20342         'iframe', 'layer',  'link',     'meta',    'object',   
20343         'script', 'style' ,'title',  'xml' // clean later..
20344 ];
20345 Roo.HtmlEditorCore.clean = [
20346     'script', 'style', 'title', 'xml'
20347 ];
20348 Roo.HtmlEditorCore.remove = [
20349     'font'
20350 ];
20351 // attributes..
20352
20353 Roo.HtmlEditorCore.ablack = [
20354     'on'
20355 ];
20356     
20357 Roo.HtmlEditorCore.aclean = [ 
20358     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20359 ];
20360
20361 // protocols..
20362 Roo.HtmlEditorCore.pwhite= [
20363         'http',  'https',  'mailto'
20364 ];
20365
20366 // white listed style attributes.
20367 Roo.HtmlEditorCore.cwhite= [
20368       //  'text-align', /// default is to allow most things..
20369       
20370          
20371 //        'font-size'//??
20372 ];
20373
20374 // black listed style attributes.
20375 Roo.HtmlEditorCore.cblack= [
20376       //  'font-size' -- this can be set by the project 
20377 ];
20378
20379
20380 Roo.HtmlEditorCore.swapCodes   =[ 
20381     [    8211, "--" ], 
20382     [    8212, "--" ], 
20383     [    8216,  "'" ],  
20384     [    8217, "'" ],  
20385     [    8220, '"' ],  
20386     [    8221, '"' ],  
20387     [    8226, "*" ],  
20388     [    8230, "..." ]
20389 ]; 
20390
20391     /*
20392  * - LGPL
20393  *
20394  * HtmlEditor
20395  * 
20396  */
20397
20398 /**
20399  * @class Roo.bootstrap.HtmlEditor
20400  * @extends Roo.bootstrap.TextArea
20401  * Bootstrap HtmlEditor class
20402
20403  * @constructor
20404  * Create a new HtmlEditor
20405  * @param {Object} config The config object
20406  */
20407
20408 Roo.bootstrap.HtmlEditor = function(config){
20409     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20410     if (!this.toolbars) {
20411         this.toolbars = [];
20412     }
20413     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20414     this.addEvents({
20415             /**
20416              * @event initialize
20417              * Fires when the editor is fully initialized (including the iframe)
20418              * @param {HtmlEditor} this
20419              */
20420             initialize: true,
20421             /**
20422              * @event activate
20423              * Fires when the editor is first receives the focus. Any insertion must wait
20424              * until after this event.
20425              * @param {HtmlEditor} this
20426              */
20427             activate: true,
20428              /**
20429              * @event beforesync
20430              * Fires before the textarea is updated with content from the editor iframe. Return false
20431              * to cancel the sync.
20432              * @param {HtmlEditor} this
20433              * @param {String} html
20434              */
20435             beforesync: true,
20436              /**
20437              * @event beforepush
20438              * Fires before the iframe editor is updated with content from the textarea. Return false
20439              * to cancel the push.
20440              * @param {HtmlEditor} this
20441              * @param {String} html
20442              */
20443             beforepush: true,
20444              /**
20445              * @event sync
20446              * Fires when the textarea is updated with content from the editor iframe.
20447              * @param {HtmlEditor} this
20448              * @param {String} html
20449              */
20450             sync: true,
20451              /**
20452              * @event push
20453              * Fires when the iframe editor is updated with content from the textarea.
20454              * @param {HtmlEditor} this
20455              * @param {String} html
20456              */
20457             push: true,
20458              /**
20459              * @event editmodechange
20460              * Fires when the editor switches edit modes
20461              * @param {HtmlEditor} this
20462              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20463              */
20464             editmodechange: true,
20465             /**
20466              * @event editorevent
20467              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20468              * @param {HtmlEditor} this
20469              */
20470             editorevent: true,
20471             /**
20472              * @event firstfocus
20473              * Fires when on first focus - needed by toolbars..
20474              * @param {HtmlEditor} this
20475              */
20476             firstfocus: true,
20477             /**
20478              * @event autosave
20479              * Auto save the htmlEditor value as a file into Events
20480              * @param {HtmlEditor} this
20481              */
20482             autosave: true,
20483             /**
20484              * @event savedpreview
20485              * preview the saved version of htmlEditor
20486              * @param {HtmlEditor} this
20487              */
20488             savedpreview: true
20489         });
20490 };
20491
20492
20493 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20494     
20495     
20496       /**
20497      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20498      */
20499     toolbars : false,
20500    
20501      /**
20502      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20503      *                        Roo.resizable.
20504      */
20505     resizable : false,
20506      /**
20507      * @cfg {Number} height (in pixels)
20508      */   
20509     height: 300,
20510    /**
20511      * @cfg {Number} width (in pixels)
20512      */   
20513     width: false,
20514     
20515     /**
20516      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20517      * 
20518      */
20519     stylesheets: false,
20520     
20521     // id of frame..
20522     frameId: false,
20523     
20524     // private properties
20525     validationEvent : false,
20526     deferHeight: true,
20527     initialized : false,
20528     activated : false,
20529     
20530     onFocus : Roo.emptyFn,
20531     iframePad:3,
20532     hideMode:'offsets',
20533     
20534     
20535     tbContainer : false,
20536     
20537     toolbarContainer :function() {
20538         return this.wrap.select('.x-html-editor-tb',true).first();
20539     },
20540
20541     /**
20542      * Protected method that will not generally be called directly. It
20543      * is called when the editor creates its toolbar. Override this method if you need to
20544      * add custom toolbar buttons.
20545      * @param {HtmlEditor} editor
20546      */
20547     createToolbar : function(){
20548         
20549         Roo.log("create toolbars");
20550         
20551         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20552         this.toolbars[0].render(this.toolbarContainer());
20553         
20554         return;
20555         
20556 //        if (!editor.toolbars || !editor.toolbars.length) {
20557 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20558 //        }
20559 //        
20560 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20561 //            editor.toolbars[i] = Roo.factory(
20562 //                    typeof(editor.toolbars[i]) == 'string' ?
20563 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20564 //                Roo.bootstrap.HtmlEditor);
20565 //            editor.toolbars[i].init(editor);
20566 //        }
20567     },
20568
20569      
20570     // private
20571     onRender : function(ct, position)
20572     {
20573        // Roo.log("Call onRender: " + this.xtype);
20574         var _t = this;
20575         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20576       
20577         this.wrap = this.inputEl().wrap({
20578             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20579         });
20580         
20581         this.editorcore.onRender(ct, position);
20582          
20583         if (this.resizable) {
20584             this.resizeEl = new Roo.Resizable(this.wrap, {
20585                 pinned : true,
20586                 wrap: true,
20587                 dynamic : true,
20588                 minHeight : this.height,
20589                 height: this.height,
20590                 handles : this.resizable,
20591                 width: this.width,
20592                 listeners : {
20593                     resize : function(r, w, h) {
20594                         _t.onResize(w,h); // -something
20595                     }
20596                 }
20597             });
20598             
20599         }
20600         this.createToolbar(this);
20601        
20602         
20603         if(!this.width && this.resizable){
20604             this.setSize(this.wrap.getSize());
20605         }
20606         if (this.resizeEl) {
20607             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20608             // should trigger onReize..
20609         }
20610         
20611     },
20612
20613     // private
20614     onResize : function(w, h)
20615     {
20616         Roo.log('resize: ' +w + ',' + h );
20617         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20618         var ew = false;
20619         var eh = false;
20620         
20621         if(this.inputEl() ){
20622             if(typeof w == 'number'){
20623                 var aw = w - this.wrap.getFrameWidth('lr');
20624                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20625                 ew = aw;
20626             }
20627             if(typeof h == 'number'){
20628                  var tbh = -11;  // fixme it needs to tool bar size!
20629                 for (var i =0; i < this.toolbars.length;i++) {
20630                     // fixme - ask toolbars for heights?
20631                     tbh += this.toolbars[i].el.getHeight();
20632                     //if (this.toolbars[i].footer) {
20633                     //    tbh += this.toolbars[i].footer.el.getHeight();
20634                     //}
20635                 }
20636               
20637                 
20638                 
20639                 
20640                 
20641                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20642                 ah -= 5; // knock a few pixes off for look..
20643                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20644                 var eh = ah;
20645             }
20646         }
20647         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20648         this.editorcore.onResize(ew,eh);
20649         
20650     },
20651
20652     /**
20653      * Toggles the editor between standard and source edit mode.
20654      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20655      */
20656     toggleSourceEdit : function(sourceEditMode)
20657     {
20658         this.editorcore.toggleSourceEdit(sourceEditMode);
20659         
20660         if(this.editorcore.sourceEditMode){
20661             Roo.log('editor - showing textarea');
20662             
20663 //            Roo.log('in');
20664 //            Roo.log(this.syncValue());
20665             this.syncValue();
20666             this.inputEl().removeClass(['hide', 'x-hidden']);
20667             this.inputEl().dom.removeAttribute('tabIndex');
20668             this.inputEl().focus();
20669         }else{
20670             Roo.log('editor - hiding textarea');
20671 //            Roo.log('out')
20672 //            Roo.log(this.pushValue()); 
20673             this.pushValue();
20674             
20675             this.inputEl().addClass(['hide', 'x-hidden']);
20676             this.inputEl().dom.setAttribute('tabIndex', -1);
20677             //this.deferFocus();
20678         }
20679          
20680         if(this.resizable){
20681             this.setSize(this.wrap.getSize());
20682         }
20683         
20684         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20685     },
20686  
20687     // private (for BoxComponent)
20688     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20689
20690     // private (for BoxComponent)
20691     getResizeEl : function(){
20692         return this.wrap;
20693     },
20694
20695     // private (for BoxComponent)
20696     getPositionEl : function(){
20697         return this.wrap;
20698     },
20699
20700     // private
20701     initEvents : function(){
20702         this.originalValue = this.getValue();
20703     },
20704
20705 //    /**
20706 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20707 //     * @method
20708 //     */
20709 //    markInvalid : Roo.emptyFn,
20710 //    /**
20711 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20712 //     * @method
20713 //     */
20714 //    clearInvalid : Roo.emptyFn,
20715
20716     setValue : function(v){
20717         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20718         this.editorcore.pushValue();
20719     },
20720
20721      
20722     // private
20723     deferFocus : function(){
20724         this.focus.defer(10, this);
20725     },
20726
20727     // doc'ed in Field
20728     focus : function(){
20729         this.editorcore.focus();
20730         
20731     },
20732       
20733
20734     // private
20735     onDestroy : function(){
20736         
20737         
20738         
20739         if(this.rendered){
20740             
20741             for (var i =0; i < this.toolbars.length;i++) {
20742                 // fixme - ask toolbars for heights?
20743                 this.toolbars[i].onDestroy();
20744             }
20745             
20746             this.wrap.dom.innerHTML = '';
20747             this.wrap.remove();
20748         }
20749     },
20750
20751     // private
20752     onFirstFocus : function(){
20753         //Roo.log("onFirstFocus");
20754         this.editorcore.onFirstFocus();
20755          for (var i =0; i < this.toolbars.length;i++) {
20756             this.toolbars[i].onFirstFocus();
20757         }
20758         
20759     },
20760     
20761     // private
20762     syncValue : function()
20763     {   
20764         this.editorcore.syncValue();
20765     },
20766     
20767     pushValue : function()
20768     {   
20769         this.editorcore.pushValue();
20770     }
20771      
20772     
20773     // hide stuff that is not compatible
20774     /**
20775      * @event blur
20776      * @hide
20777      */
20778     /**
20779      * @event change
20780      * @hide
20781      */
20782     /**
20783      * @event focus
20784      * @hide
20785      */
20786     /**
20787      * @event specialkey
20788      * @hide
20789      */
20790     /**
20791      * @cfg {String} fieldClass @hide
20792      */
20793     /**
20794      * @cfg {String} focusClass @hide
20795      */
20796     /**
20797      * @cfg {String} autoCreate @hide
20798      */
20799     /**
20800      * @cfg {String} inputType @hide
20801      */
20802     /**
20803      * @cfg {String} invalidClass @hide
20804      */
20805     /**
20806      * @cfg {String} invalidText @hide
20807      */
20808     /**
20809      * @cfg {String} msgFx @hide
20810      */
20811     /**
20812      * @cfg {String} validateOnBlur @hide
20813      */
20814 });
20815  
20816     
20817    
20818    
20819    
20820       
20821 Roo.namespace('Roo.bootstrap.htmleditor');
20822 /**
20823  * @class Roo.bootstrap.HtmlEditorToolbar1
20824  * Basic Toolbar
20825  * 
20826  * Usage:
20827  *
20828  new Roo.bootstrap.HtmlEditor({
20829     ....
20830     toolbars : [
20831         new Roo.bootstrap.HtmlEditorToolbar1({
20832             disable : { fonts: 1 , format: 1, ..., ... , ...],
20833             btns : [ .... ]
20834         })
20835     }
20836      
20837  * 
20838  * @cfg {Object} disable List of elements to disable..
20839  * @cfg {Array} btns List of additional buttons.
20840  * 
20841  * 
20842  * NEEDS Extra CSS? 
20843  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20844  */
20845  
20846 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20847 {
20848     
20849     Roo.apply(this, config);
20850     
20851     // default disabled, based on 'good practice'..
20852     this.disable = this.disable || {};
20853     Roo.applyIf(this.disable, {
20854         fontSize : true,
20855         colors : true,
20856         specialElements : true
20857     });
20858     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20859     
20860     this.editor = config.editor;
20861     this.editorcore = config.editor.editorcore;
20862     
20863     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20864     
20865     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20866     // dont call parent... till later.
20867 }
20868 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20869      
20870     bar : true,
20871     
20872     editor : false,
20873     editorcore : false,
20874     
20875     
20876     formats : [
20877         "p" ,  
20878         "h1","h2","h3","h4","h5","h6", 
20879         "pre", "code", 
20880         "abbr", "acronym", "address", "cite", "samp", "var",
20881         'div','span'
20882     ],
20883     
20884     onRender : function(ct, position)
20885     {
20886        // Roo.log("Call onRender: " + this.xtype);
20887         
20888        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20889        Roo.log(this.el);
20890        this.el.dom.style.marginBottom = '0';
20891        var _this = this;
20892        var editorcore = this.editorcore;
20893        var editor= this.editor;
20894        
20895        var children = [];
20896        var btn = function(id,cmd , toggle, handler){
20897        
20898             var  event = toggle ? 'toggle' : 'click';
20899        
20900             var a = {
20901                 size : 'sm',
20902                 xtype: 'Button',
20903                 xns: Roo.bootstrap,
20904                 glyphicon : id,
20905                 cmd : id || cmd,
20906                 enableToggle:toggle !== false,
20907                 //html : 'submit'
20908                 pressed : toggle ? false : null,
20909                 listeners : {}
20910             };
20911             a.listeners[toggle ? 'toggle' : 'click'] = function() {
20912                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
20913             };
20914             children.push(a);
20915             return a;
20916        }
20917         
20918         var style = {
20919                 xtype: 'Button',
20920                 size : 'sm',
20921                 xns: Roo.bootstrap,
20922                 glyphicon : 'font',
20923                 //html : 'submit'
20924                 menu : {
20925                     xtype: 'Menu',
20926                     xns: Roo.bootstrap,
20927                     items:  []
20928                 }
20929         };
20930         Roo.each(this.formats, function(f) {
20931             style.menu.items.push({
20932                 xtype :'MenuItem',
20933                 xns: Roo.bootstrap,
20934                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
20935                 tagname : f,
20936                 listeners : {
20937                     click : function()
20938                     {
20939                         editorcore.insertTag(this.tagname);
20940                         editor.focus();
20941                     }
20942                 }
20943                 
20944             });
20945         });
20946          children.push(style);   
20947             
20948             
20949         btn('bold',false,true);
20950         btn('italic',false,true);
20951         btn('align-left', 'justifyleft',true);
20952         btn('align-center', 'justifycenter',true);
20953         btn('align-right' , 'justifyright',true);
20954         btn('link', false, false, function(btn) {
20955             //Roo.log("create link?");
20956             var url = prompt(this.createLinkText, this.defaultLinkValue);
20957             if(url && url != 'http:/'+'/'){
20958                 this.editorcore.relayCmd('createlink', url);
20959             }
20960         }),
20961         btn('list','insertunorderedlist',true);
20962         btn('pencil', false,true, function(btn){
20963                 Roo.log(this);
20964                 
20965                 this.toggleSourceEdit(btn.pressed);
20966         });
20967         /*
20968         var cog = {
20969                 xtype: 'Button',
20970                 size : 'sm',
20971                 xns: Roo.bootstrap,
20972                 glyphicon : 'cog',
20973                 //html : 'submit'
20974                 menu : {
20975                     xtype: 'Menu',
20976                     xns: Roo.bootstrap,
20977                     items:  []
20978                 }
20979         };
20980         
20981         cog.menu.items.push({
20982             xtype :'MenuItem',
20983             xns: Roo.bootstrap,
20984             html : Clean styles,
20985             tagname : f,
20986             listeners : {
20987                 click : function()
20988                 {
20989                     editorcore.insertTag(this.tagname);
20990                     editor.focus();
20991                 }
20992             }
20993             
20994         });
20995        */
20996         
20997          
20998        this.xtype = 'NavSimplebar';
20999         
21000         for(var i=0;i< children.length;i++) {
21001             
21002             this.buttons.add(this.addxtypeChild(children[i]));
21003             
21004         }
21005         
21006         editor.on('editorevent', this.updateToolbar, this);
21007     },
21008     onBtnClick : function(id)
21009     {
21010        this.editorcore.relayCmd(id);
21011        this.editorcore.focus();
21012     },
21013     
21014     /**
21015      * Protected method that will not generally be called directly. It triggers
21016      * a toolbar update by reading the markup state of the current selection in the editor.
21017      */
21018     updateToolbar: function(){
21019
21020         if(!this.editorcore.activated){
21021             this.editor.onFirstFocus(); // is this neeed?
21022             return;
21023         }
21024
21025         var btns = this.buttons; 
21026         var doc = this.editorcore.doc;
21027         btns.get('bold').setActive(doc.queryCommandState('bold'));
21028         btns.get('italic').setActive(doc.queryCommandState('italic'));
21029         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21030         
21031         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21032         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21033         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21034         
21035         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21036         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21037          /*
21038         
21039         var ans = this.editorcore.getAllAncestors();
21040         if (this.formatCombo) {
21041             
21042             
21043             var store = this.formatCombo.store;
21044             this.formatCombo.setValue("");
21045             for (var i =0; i < ans.length;i++) {
21046                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21047                     // select it..
21048                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21049                     break;
21050                 }
21051             }
21052         }
21053         
21054         
21055         
21056         // hides menus... - so this cant be on a menu...
21057         Roo.bootstrap.MenuMgr.hideAll();
21058         */
21059         Roo.bootstrap.MenuMgr.hideAll();
21060         //this.editorsyncValue();
21061     },
21062     onFirstFocus: function() {
21063         this.buttons.each(function(item){
21064            item.enable();
21065         });
21066     },
21067     toggleSourceEdit : function(sourceEditMode){
21068         
21069           
21070         if(sourceEditMode){
21071             Roo.log("disabling buttons");
21072            this.buttons.each( function(item){
21073                 if(item.cmd != 'pencil'){
21074                     item.disable();
21075                 }
21076             });
21077           
21078         }else{
21079             Roo.log("enabling buttons");
21080             if(this.editorcore.initialized){
21081                 this.buttons.each( function(item){
21082                     item.enable();
21083                 });
21084             }
21085             
21086         }
21087         Roo.log("calling toggole on editor");
21088         // tell the editor that it's been pressed..
21089         this.editor.toggleSourceEdit(sourceEditMode);
21090        
21091     }
21092 });
21093
21094
21095
21096
21097
21098 /**
21099  * @class Roo.bootstrap.Table.AbstractSelectionModel
21100  * @extends Roo.util.Observable
21101  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21102  * implemented by descendant classes.  This class should not be directly instantiated.
21103  * @constructor
21104  */
21105 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21106     this.locked = false;
21107     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21108 };
21109
21110
21111 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21112     /** @ignore Called by the grid automatically. Do not call directly. */
21113     init : function(grid){
21114         this.grid = grid;
21115         this.initEvents();
21116     },
21117
21118     /**
21119      * Locks the selections.
21120      */
21121     lock : function(){
21122         this.locked = true;
21123     },
21124
21125     /**
21126      * Unlocks the selections.
21127      */
21128     unlock : function(){
21129         this.locked = false;
21130     },
21131
21132     /**
21133      * Returns true if the selections are locked.
21134      * @return {Boolean}
21135      */
21136     isLocked : function(){
21137         return this.locked;
21138     }
21139 });
21140 /**
21141  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21142  * @class Roo.bootstrap.Table.RowSelectionModel
21143  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21144  * It supports multiple selections and keyboard selection/navigation. 
21145  * @constructor
21146  * @param {Object} config
21147  */
21148
21149 Roo.bootstrap.Table.RowSelectionModel = function(config){
21150     Roo.apply(this, config);
21151     this.selections = new Roo.util.MixedCollection(false, function(o){
21152         return o.id;
21153     });
21154
21155     this.last = false;
21156     this.lastActive = false;
21157
21158     this.addEvents({
21159         /**
21160              * @event selectionchange
21161              * Fires when the selection changes
21162              * @param {SelectionModel} this
21163              */
21164             "selectionchange" : true,
21165         /**
21166              * @event afterselectionchange
21167              * Fires after the selection changes (eg. by key press or clicking)
21168              * @param {SelectionModel} this
21169              */
21170             "afterselectionchange" : true,
21171         /**
21172              * @event beforerowselect
21173              * Fires when a row is selected being selected, return false to cancel.
21174              * @param {SelectionModel} this
21175              * @param {Number} rowIndex The selected index
21176              * @param {Boolean} keepExisting False if other selections will be cleared
21177              */
21178             "beforerowselect" : true,
21179         /**
21180              * @event rowselect
21181              * Fires when a row is selected.
21182              * @param {SelectionModel} this
21183              * @param {Number} rowIndex The selected index
21184              * @param {Roo.data.Record} r The record
21185              */
21186             "rowselect" : true,
21187         /**
21188              * @event rowdeselect
21189              * Fires when a row is deselected.
21190              * @param {SelectionModel} this
21191              * @param {Number} rowIndex The selected index
21192              */
21193         "rowdeselect" : true
21194     });
21195     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21196     this.locked = false;
21197 };
21198
21199 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21200     /**
21201      * @cfg {Boolean} singleSelect
21202      * True to allow selection of only one row at a time (defaults to false)
21203      */
21204     singleSelect : false,
21205
21206     // private
21207     initEvents : function(){
21208
21209         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21210             this.grid.on("mousedown", this.handleMouseDown, this);
21211         }else{ // allow click to work like normal
21212             this.grid.on("rowclick", this.handleDragableRowClick, this);
21213         }
21214
21215         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21216             "up" : function(e){
21217                 if(!e.shiftKey){
21218                     this.selectPrevious(e.shiftKey);
21219                 }else if(this.last !== false && this.lastActive !== false){
21220                     var last = this.last;
21221                     this.selectRange(this.last,  this.lastActive-1);
21222                     this.grid.getView().focusRow(this.lastActive);
21223                     if(last !== false){
21224                         this.last = last;
21225                     }
21226                 }else{
21227                     this.selectFirstRow();
21228                 }
21229                 this.fireEvent("afterselectionchange", this);
21230             },
21231             "down" : function(e){
21232                 if(!e.shiftKey){
21233                     this.selectNext(e.shiftKey);
21234                 }else if(this.last !== false && this.lastActive !== false){
21235                     var last = this.last;
21236                     this.selectRange(this.last,  this.lastActive+1);
21237                     this.grid.getView().focusRow(this.lastActive);
21238                     if(last !== false){
21239                         this.last = last;
21240                     }
21241                 }else{
21242                     this.selectFirstRow();
21243                 }
21244                 this.fireEvent("afterselectionchange", this);
21245             },
21246             scope: this
21247         });
21248
21249         var view = this.grid.view;
21250         view.on("refresh", this.onRefresh, this);
21251         view.on("rowupdated", this.onRowUpdated, this);
21252         view.on("rowremoved", this.onRemove, this);
21253     },
21254
21255     // private
21256     onRefresh : function(){
21257         var ds = this.grid.dataSource, i, v = this.grid.view;
21258         var s = this.selections;
21259         s.each(function(r){
21260             if((i = ds.indexOfId(r.id)) != -1){
21261                 v.onRowSelect(i);
21262             }else{
21263                 s.remove(r);
21264             }
21265         });
21266     },
21267
21268     // private
21269     onRemove : function(v, index, r){
21270         this.selections.remove(r);
21271     },
21272
21273     // private
21274     onRowUpdated : function(v, index, r){
21275         if(this.isSelected(r)){
21276             v.onRowSelect(index);
21277         }
21278     },
21279
21280     /**
21281      * Select records.
21282      * @param {Array} records The records to select
21283      * @param {Boolean} keepExisting (optional) True to keep existing selections
21284      */
21285     selectRecords : function(records, keepExisting){
21286         if(!keepExisting){
21287             this.clearSelections();
21288         }
21289         var ds = this.grid.dataSource;
21290         for(var i = 0, len = records.length; i < len; i++){
21291             this.selectRow(ds.indexOf(records[i]), true);
21292         }
21293     },
21294
21295     /**
21296      * Gets the number of selected rows.
21297      * @return {Number}
21298      */
21299     getCount : function(){
21300         return this.selections.length;
21301     },
21302
21303     /**
21304      * Selects the first row in the grid.
21305      */
21306     selectFirstRow : function(){
21307         this.selectRow(0);
21308     },
21309
21310     /**
21311      * Select the last row.
21312      * @param {Boolean} keepExisting (optional) True to keep existing selections
21313      */
21314     selectLastRow : function(keepExisting){
21315         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21316     },
21317
21318     /**
21319      * Selects the row immediately following the last selected row.
21320      * @param {Boolean} keepExisting (optional) True to keep existing selections
21321      */
21322     selectNext : function(keepExisting){
21323         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21324             this.selectRow(this.last+1, keepExisting);
21325             this.grid.getView().focusRow(this.last);
21326         }
21327     },
21328
21329     /**
21330      * Selects the row that precedes the last selected row.
21331      * @param {Boolean} keepExisting (optional) True to keep existing selections
21332      */
21333     selectPrevious : function(keepExisting){
21334         if(this.last){
21335             this.selectRow(this.last-1, keepExisting);
21336             this.grid.getView().focusRow(this.last);
21337         }
21338     },
21339
21340     /**
21341      * Returns the selected records
21342      * @return {Array} Array of selected records
21343      */
21344     getSelections : function(){
21345         return [].concat(this.selections.items);
21346     },
21347
21348     /**
21349      * Returns the first selected record.
21350      * @return {Record}
21351      */
21352     getSelected : function(){
21353         return this.selections.itemAt(0);
21354     },
21355
21356
21357     /**
21358      * Clears all selections.
21359      */
21360     clearSelections : function(fast){
21361         if(this.locked) return;
21362         if(fast !== true){
21363             var ds = this.grid.dataSource;
21364             var s = this.selections;
21365             s.each(function(r){
21366                 this.deselectRow(ds.indexOfId(r.id));
21367             }, this);
21368             s.clear();
21369         }else{
21370             this.selections.clear();
21371         }
21372         this.last = false;
21373     },
21374
21375
21376     /**
21377      * Selects all rows.
21378      */
21379     selectAll : function(){
21380         if(this.locked) return;
21381         this.selections.clear();
21382         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21383             this.selectRow(i, true);
21384         }
21385     },
21386
21387     /**
21388      * Returns True if there is a selection.
21389      * @return {Boolean}
21390      */
21391     hasSelection : function(){
21392         return this.selections.length > 0;
21393     },
21394
21395     /**
21396      * Returns True if the specified row is selected.
21397      * @param {Number/Record} record The record or index of the record to check
21398      * @return {Boolean}
21399      */
21400     isSelected : function(index){
21401         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21402         return (r && this.selections.key(r.id) ? true : false);
21403     },
21404
21405     /**
21406      * Returns True if the specified record id is selected.
21407      * @param {String} id The id of record to check
21408      * @return {Boolean}
21409      */
21410     isIdSelected : function(id){
21411         return (this.selections.key(id) ? true : false);
21412     },
21413
21414     // private
21415     handleMouseDown : function(e, t){
21416         var view = this.grid.getView(), rowIndex;
21417         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21418             return;
21419         };
21420         if(e.shiftKey && this.last !== false){
21421             var last = this.last;
21422             this.selectRange(last, rowIndex, e.ctrlKey);
21423             this.last = last; // reset the last
21424             view.focusRow(rowIndex);
21425         }else{
21426             var isSelected = this.isSelected(rowIndex);
21427             if(e.button !== 0 && isSelected){
21428                 view.focusRow(rowIndex);
21429             }else if(e.ctrlKey && isSelected){
21430                 this.deselectRow(rowIndex);
21431             }else if(!isSelected){
21432                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21433                 view.focusRow(rowIndex);
21434             }
21435         }
21436         this.fireEvent("afterselectionchange", this);
21437     },
21438     // private
21439     handleDragableRowClick :  function(grid, rowIndex, e) 
21440     {
21441         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21442             this.selectRow(rowIndex, false);
21443             grid.view.focusRow(rowIndex);
21444              this.fireEvent("afterselectionchange", this);
21445         }
21446     },
21447     
21448     /**
21449      * Selects multiple rows.
21450      * @param {Array} rows Array of the indexes of the row to select
21451      * @param {Boolean} keepExisting (optional) True to keep existing selections
21452      */
21453     selectRows : function(rows, keepExisting){
21454         if(!keepExisting){
21455             this.clearSelections();
21456         }
21457         for(var i = 0, len = rows.length; i < len; i++){
21458             this.selectRow(rows[i], true);
21459         }
21460     },
21461
21462     /**
21463      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21464      * @param {Number} startRow The index of the first row in the range
21465      * @param {Number} endRow The index of the last row in the range
21466      * @param {Boolean} keepExisting (optional) True to retain existing selections
21467      */
21468     selectRange : function(startRow, endRow, keepExisting){
21469         if(this.locked) return;
21470         if(!keepExisting){
21471             this.clearSelections();
21472         }
21473         if(startRow <= endRow){
21474             for(var i = startRow; i <= endRow; i++){
21475                 this.selectRow(i, true);
21476             }
21477         }else{
21478             for(var i = startRow; i >= endRow; i--){
21479                 this.selectRow(i, true);
21480             }
21481         }
21482     },
21483
21484     /**
21485      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21486      * @param {Number} startRow The index of the first row in the range
21487      * @param {Number} endRow The index of the last row in the range
21488      */
21489     deselectRange : function(startRow, endRow, preventViewNotify){
21490         if(this.locked) return;
21491         for(var i = startRow; i <= endRow; i++){
21492             this.deselectRow(i, preventViewNotify);
21493         }
21494     },
21495
21496     /**
21497      * Selects a row.
21498      * @param {Number} row The index of the row to select
21499      * @param {Boolean} keepExisting (optional) True to keep existing selections
21500      */
21501     selectRow : function(index, keepExisting, preventViewNotify){
21502         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21503         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21504             if(!keepExisting || this.singleSelect){
21505                 this.clearSelections();
21506             }
21507             var r = this.grid.dataSource.getAt(index);
21508             this.selections.add(r);
21509             this.last = this.lastActive = index;
21510             if(!preventViewNotify){
21511                 this.grid.getView().onRowSelect(index);
21512             }
21513             this.fireEvent("rowselect", this, index, r);
21514             this.fireEvent("selectionchange", this);
21515         }
21516     },
21517
21518     /**
21519      * Deselects a row.
21520      * @param {Number} row The index of the row to deselect
21521      */
21522     deselectRow : function(index, preventViewNotify){
21523         if(this.locked) return;
21524         if(this.last == index){
21525             this.last = false;
21526         }
21527         if(this.lastActive == index){
21528             this.lastActive = false;
21529         }
21530         var r = this.grid.dataSource.getAt(index);
21531         this.selections.remove(r);
21532         if(!preventViewNotify){
21533             this.grid.getView().onRowDeselect(index);
21534         }
21535         this.fireEvent("rowdeselect", this, index);
21536         this.fireEvent("selectionchange", this);
21537     },
21538
21539     // private
21540     restoreLast : function(){
21541         if(this._last){
21542             this.last = this._last;
21543         }
21544     },
21545
21546     // private
21547     acceptsNav : function(row, col, cm){
21548         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21549     },
21550
21551     // private
21552     onEditorKey : function(field, e){
21553         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21554         if(k == e.TAB){
21555             e.stopEvent();
21556             ed.completeEdit();
21557             if(e.shiftKey){
21558                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21559             }else{
21560                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21561             }
21562         }else if(k == e.ENTER && !e.ctrlKey){
21563             e.stopEvent();
21564             ed.completeEdit();
21565             if(e.shiftKey){
21566                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21567             }else{
21568                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21569             }
21570         }else if(k == e.ESC){
21571             ed.cancelEdit();
21572         }
21573         if(newCell){
21574             g.startEditing(newCell[0], newCell[1]);
21575         }
21576     }
21577 });/*
21578  * Based on:
21579  * Ext JS Library 1.1.1
21580  * Copyright(c) 2006-2007, Ext JS, LLC.
21581  *
21582  * Originally Released Under LGPL - original licence link has changed is not relivant.
21583  *
21584  * Fork - LGPL
21585  * <script type="text/javascript">
21586  */
21587  
21588 /**
21589  * @class Roo.bootstrap.PagingToolbar
21590  * @extends Roo.Row
21591  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21592  * @constructor
21593  * Create a new PagingToolbar
21594  * @param {Object} config The config object
21595  */
21596 Roo.bootstrap.PagingToolbar = function(config)
21597 {
21598     // old args format still supported... - xtype is prefered..
21599         // created from xtype...
21600     var ds = config.dataSource;
21601     this.toolbarItems = [];
21602     if (config.items) {
21603         this.toolbarItems = config.items;
21604 //        config.items = [];
21605     }
21606     
21607     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21608     this.ds = ds;
21609     this.cursor = 0;
21610     if (ds) { 
21611         this.bind(ds);
21612     }
21613     
21614     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21615     
21616 };
21617
21618 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21619     /**
21620      * @cfg {Roo.data.Store} dataSource
21621      * The underlying data store providing the paged data
21622      */
21623     /**
21624      * @cfg {String/HTMLElement/Element} container
21625      * container The id or element that will contain the toolbar
21626      */
21627     /**
21628      * @cfg {Boolean} displayInfo
21629      * True to display the displayMsg (defaults to false)
21630      */
21631     /**
21632      * @cfg {Number} pageSize
21633      * The number of records to display per page (defaults to 20)
21634      */
21635     pageSize: 20,
21636     /**
21637      * @cfg {String} displayMsg
21638      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21639      */
21640     displayMsg : 'Displaying {0} - {1} of {2}',
21641     /**
21642      * @cfg {String} emptyMsg
21643      * The message to display when no records are found (defaults to "No data to display")
21644      */
21645     emptyMsg : 'No data to display',
21646     /**
21647      * Customizable piece of the default paging text (defaults to "Page")
21648      * @type String
21649      */
21650     beforePageText : "Page",
21651     /**
21652      * Customizable piece of the default paging text (defaults to "of %0")
21653      * @type String
21654      */
21655     afterPageText : "of {0}",
21656     /**
21657      * Customizable piece of the default paging text (defaults to "First Page")
21658      * @type String
21659      */
21660     firstText : "First Page",
21661     /**
21662      * Customizable piece of the default paging text (defaults to "Previous Page")
21663      * @type String
21664      */
21665     prevText : "Previous Page",
21666     /**
21667      * Customizable piece of the default paging text (defaults to "Next Page")
21668      * @type String
21669      */
21670     nextText : "Next Page",
21671     /**
21672      * Customizable piece of the default paging text (defaults to "Last Page")
21673      * @type String
21674      */
21675     lastText : "Last Page",
21676     /**
21677      * Customizable piece of the default paging text (defaults to "Refresh")
21678      * @type String
21679      */
21680     refreshText : "Refresh",
21681
21682     buttons : false,
21683     // private
21684     onRender : function(ct, position) 
21685     {
21686         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21687         this.navgroup.parentId = this.id;
21688         this.navgroup.onRender(this.el, null);
21689         // add the buttons to the navgroup
21690         
21691         if(this.displayInfo){
21692             Roo.log(this.el.select('ul.navbar-nav',true).first());
21693             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21694             this.displayEl = this.el.select('.x-paging-info', true).first();
21695 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21696 //            this.displayEl = navel.el.select('span',true).first();
21697         }
21698         
21699         var _this = this;
21700         
21701         if(this.buttons){
21702             Roo.each(_this.buttons, function(e){
21703                Roo.factory(e).onRender(_this.el, null);
21704             });
21705         }
21706             
21707         Roo.each(_this.toolbarItems, function(e) {
21708             _this.navgroup.addItem(e);
21709         });
21710         
21711         
21712         this.first = this.navgroup.addItem({
21713             tooltip: this.firstText,
21714             cls: "prev",
21715             icon : 'fa fa-backward',
21716             disabled: true,
21717             preventDefault: true,
21718             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21719         });
21720         
21721         this.prev =  this.navgroup.addItem({
21722             tooltip: this.prevText,
21723             cls: "prev",
21724             icon : 'fa fa-step-backward',
21725             disabled: true,
21726             preventDefault: true,
21727             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21728         });
21729     //this.addSeparator();
21730         
21731         
21732         var field = this.navgroup.addItem( {
21733             tagtype : 'span',
21734             cls : 'x-paging-position',
21735             
21736             html : this.beforePageText  +
21737                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21738                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21739          } ); //?? escaped?
21740         
21741         this.field = field.el.select('input', true).first();
21742         this.field.on("keydown", this.onPagingKeydown, this);
21743         this.field.on("focus", function(){this.dom.select();});
21744     
21745     
21746         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21747         //this.field.setHeight(18);
21748         //this.addSeparator();
21749         this.next = this.navgroup.addItem({
21750             tooltip: this.nextText,
21751             cls: "next",
21752             html : ' <i class="fa fa-step-forward">',
21753             disabled: true,
21754             preventDefault: true,
21755             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21756         });
21757         this.last = this.navgroup.addItem({
21758             tooltip: this.lastText,
21759             icon : 'fa fa-forward',
21760             cls: "next",
21761             disabled: true,
21762             preventDefault: true,
21763             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21764         });
21765     //this.addSeparator();
21766         this.loading = this.navgroup.addItem({
21767             tooltip: this.refreshText,
21768             icon: 'fa fa-refresh',
21769             preventDefault: true,
21770             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21771         });
21772
21773     },
21774
21775     // private
21776     updateInfo : function(){
21777         if(this.displayEl){
21778             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21779             var msg = count == 0 ?
21780                 this.emptyMsg :
21781                 String.format(
21782                     this.displayMsg,
21783                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21784                 );
21785             this.displayEl.update(msg);
21786         }
21787     },
21788
21789     // private
21790     onLoad : function(ds, r, o){
21791        this.cursor = o.params ? o.params.start : 0;
21792        var d = this.getPageData(),
21793             ap = d.activePage,
21794             ps = d.pages;
21795         
21796        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21797        this.field.dom.value = ap;
21798        this.first.setDisabled(ap == 1);
21799        this.prev.setDisabled(ap == 1);
21800        this.next.setDisabled(ap == ps);
21801        this.last.setDisabled(ap == ps);
21802        this.loading.enable();
21803        this.updateInfo();
21804     },
21805
21806     // private
21807     getPageData : function(){
21808         var total = this.ds.getTotalCount();
21809         return {
21810             total : total,
21811             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21812             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21813         };
21814     },
21815
21816     // private
21817     onLoadError : function(){
21818         this.loading.enable();
21819     },
21820
21821     // private
21822     onPagingKeydown : function(e){
21823         var k = e.getKey();
21824         var d = this.getPageData();
21825         if(k == e.RETURN){
21826             var v = this.field.dom.value, pageNum;
21827             if(!v || isNaN(pageNum = parseInt(v, 10))){
21828                 this.field.dom.value = d.activePage;
21829                 return;
21830             }
21831             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21832             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21833             e.stopEvent();
21834         }
21835         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))
21836         {
21837           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21838           this.field.dom.value = pageNum;
21839           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21840           e.stopEvent();
21841         }
21842         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21843         {
21844           var v = this.field.dom.value, pageNum; 
21845           var increment = (e.shiftKey) ? 10 : 1;
21846           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21847             increment *= -1;
21848           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21849             this.field.dom.value = d.activePage;
21850             return;
21851           }
21852           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21853           {
21854             this.field.dom.value = parseInt(v, 10) + increment;
21855             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21856             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21857           }
21858           e.stopEvent();
21859         }
21860     },
21861
21862     // private
21863     beforeLoad : function(){
21864         if(this.loading){
21865             this.loading.disable();
21866         }
21867     },
21868
21869     // private
21870     onClick : function(which){
21871         
21872         var ds = this.ds;
21873         if (!ds) {
21874             return;
21875         }
21876         
21877         switch(which){
21878             case "first":
21879                 ds.load({params:{start: 0, limit: this.pageSize}});
21880             break;
21881             case "prev":
21882                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21883             break;
21884             case "next":
21885                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21886             break;
21887             case "last":
21888                 var total = ds.getTotalCount();
21889                 var extra = total % this.pageSize;
21890                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21891                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21892             break;
21893             case "refresh":
21894                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
21895             break;
21896         }
21897     },
21898
21899     /**
21900      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
21901      * @param {Roo.data.Store} store The data store to unbind
21902      */
21903     unbind : function(ds){
21904         ds.un("beforeload", this.beforeLoad, this);
21905         ds.un("load", this.onLoad, this);
21906         ds.un("loadexception", this.onLoadError, this);
21907         ds.un("remove", this.updateInfo, this);
21908         ds.un("add", this.updateInfo, this);
21909         this.ds = undefined;
21910     },
21911
21912     /**
21913      * Binds the paging toolbar to the specified {@link Roo.data.Store}
21914      * @param {Roo.data.Store} store The data store to bind
21915      */
21916     bind : function(ds){
21917         ds.on("beforeload", this.beforeLoad, this);
21918         ds.on("load", this.onLoad, this);
21919         ds.on("loadexception", this.onLoadError, this);
21920         ds.on("remove", this.updateInfo, this);
21921         ds.on("add", this.updateInfo, this);
21922         this.ds = ds;
21923     }
21924 });/*
21925  * - LGPL
21926  *
21927  * element
21928  * 
21929  */
21930
21931 /**
21932  * @class Roo.bootstrap.MessageBar
21933  * @extends Roo.bootstrap.Component
21934  * Bootstrap MessageBar class
21935  * @cfg {String} html contents of the MessageBar
21936  * @cfg {String} weight (info | success | warning | danger) default info
21937  * @cfg {String} beforeClass insert the bar before the given class
21938  * @cfg {Boolean} closable (true | false) default false
21939  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
21940  * 
21941  * @constructor
21942  * Create a new Element
21943  * @param {Object} config The config object
21944  */
21945
21946 Roo.bootstrap.MessageBar = function(config){
21947     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
21948 };
21949
21950 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
21951     
21952     html: '',
21953     weight: 'info',
21954     closable: false,
21955     fixed: false,
21956     beforeClass: 'bootstrap-sticky-wrap',
21957     
21958     getAutoCreate : function(){
21959         
21960         var cfg = {
21961             tag: 'div',
21962             cls: 'alert alert-dismissable alert-' + this.weight,
21963             cn: [
21964                 {
21965                     tag: 'span',
21966                     cls: 'message',
21967                     html: this.html || ''
21968                 }
21969             ]
21970         }
21971         
21972         if(this.fixed){
21973             cfg.cls += ' alert-messages-fixed';
21974         }
21975         
21976         if(this.closable){
21977             cfg.cn.push({
21978                 tag: 'button',
21979                 cls: 'close',
21980                 html: 'x'
21981             });
21982         }
21983         
21984         return cfg;
21985     },
21986     
21987     onRender : function(ct, position)
21988     {
21989         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21990         
21991         if(!this.el){
21992             var cfg = Roo.apply({},  this.getAutoCreate());
21993             cfg.id = Roo.id();
21994             
21995             if (this.cls) {
21996                 cfg.cls += ' ' + this.cls;
21997             }
21998             if (this.style) {
21999                 cfg.style = this.style;
22000             }
22001             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22002             
22003             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22004         }
22005         
22006         this.el.select('>button.close').on('click', this.hide, this);
22007         
22008     },
22009     
22010     show : function()
22011     {
22012         if (!this.rendered) {
22013             this.render();
22014         }
22015         
22016         this.el.show();
22017         
22018         this.fireEvent('show', this);
22019         
22020     },
22021     
22022     hide : function()
22023     {
22024         if (!this.rendered) {
22025             this.render();
22026         }
22027         
22028         this.el.hide();
22029         
22030         this.fireEvent('hide', this);
22031     },
22032     
22033     update : function()
22034     {
22035 //        var e = this.el.dom.firstChild;
22036 //        
22037 //        if(this.closable){
22038 //            e = e.nextSibling;
22039 //        }
22040 //        
22041 //        e.data = this.html || '';
22042
22043         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22044     }
22045    
22046 });
22047
22048  
22049
22050      /*
22051  * - LGPL
22052  *
22053  * Graph
22054  * 
22055  */
22056
22057
22058 /**
22059  * @class Roo.bootstrap.Graph
22060  * @extends Roo.bootstrap.Component
22061  * Bootstrap Graph class
22062 > Prameters
22063  -sm {number} sm 4
22064  -md {number} md 5
22065  @cfg {String} graphtype  bar | vbar | pie
22066  @cfg {number} g_x coodinator | centre x (pie)
22067  @cfg {number} g_y coodinator | centre y (pie)
22068  @cfg {number} g_r radius (pie)
22069  @cfg {number} g_height height of the chart (respected by all elements in the set)
22070  @cfg {number} g_width width of the chart (respected by all elements in the set)
22071  @cfg {Object} title The title of the chart
22072     
22073  -{Array}  values
22074  -opts (object) options for the chart 
22075      o {
22076      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22077      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22078      o vgutter (number)
22079      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.
22080      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22081      o to
22082      o stretch (boolean)
22083      o }
22084  -opts (object) options for the pie
22085      o{
22086      o cut
22087      o startAngle (number)
22088      o endAngle (number)
22089      } 
22090  *
22091  * @constructor
22092  * Create a new Input
22093  * @param {Object} config The config object
22094  */
22095
22096 Roo.bootstrap.Graph = function(config){
22097     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22098     
22099     this.addEvents({
22100         // img events
22101         /**
22102          * @event click
22103          * The img click event for the img.
22104          * @param {Roo.EventObject} e
22105          */
22106         "click" : true
22107     });
22108 };
22109
22110 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22111     
22112     sm: 4,
22113     md: 5,
22114     graphtype: 'bar',
22115     g_height: 250,
22116     g_width: 400,
22117     g_x: 50,
22118     g_y: 50,
22119     g_r: 30,
22120     opts:{
22121         //g_colors: this.colors,
22122         g_type: 'soft',
22123         g_gutter: '20%'
22124
22125     },
22126     title : false,
22127
22128     getAutoCreate : function(){
22129         
22130         var cfg = {
22131             tag: 'div',
22132             html : null
22133         }
22134         
22135         
22136         return  cfg;
22137     },
22138
22139     onRender : function(ct,position){
22140         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22141         this.raphael = Raphael(this.el.dom);
22142         
22143                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22144                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22145                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22146                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22147                 /*
22148                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22149                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22150                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22151                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22152                 
22153                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22154                 r.barchart(330, 10, 300, 220, data1);
22155                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22156                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22157                 */
22158                 
22159                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22160                 // r.barchart(30, 30, 560, 250,  xdata, {
22161                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22162                 //     axis : "0 0 1 1",
22163                 //     axisxlabels :  xdata
22164                 //     //yvalues : cols,
22165                    
22166                 // });
22167 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22168 //        
22169 //        this.load(null,xdata,{
22170 //                axis : "0 0 1 1",
22171 //                axisxlabels :  xdata
22172 //                });
22173
22174     },
22175
22176     load : function(graphtype,xdata,opts){
22177         this.raphael.clear();
22178         if(!graphtype) {
22179             graphtype = this.graphtype;
22180         }
22181         if(!opts){
22182             opts = this.opts;
22183         }
22184         var r = this.raphael,
22185             fin = function () {
22186                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22187             },
22188             fout = function () {
22189                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22190             },
22191             pfin = function() {
22192                 this.sector.stop();
22193                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22194
22195                 if (this.label) {
22196                     this.label[0].stop();
22197                     this.label[0].attr({ r: 7.5 });
22198                     this.label[1].attr({ "font-weight": 800 });
22199                 }
22200             },
22201             pfout = function() {
22202                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22203
22204                 if (this.label) {
22205                     this.label[0].animate({ r: 5 }, 500, "bounce");
22206                     this.label[1].attr({ "font-weight": 400 });
22207                 }
22208             };
22209
22210         switch(graphtype){
22211             case 'bar':
22212                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22213                 break;
22214             case 'hbar':
22215                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22216                 break;
22217             case 'pie':
22218 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22219 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22220 //            
22221                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22222                 
22223                 break;
22224
22225         }
22226         
22227         if(this.title){
22228             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22229         }
22230         
22231     },
22232     
22233     setTitle: function(o)
22234     {
22235         this.title = o;
22236     },
22237     
22238     initEvents: function() {
22239         
22240         if(!this.href){
22241             this.el.on('click', this.onClick, this);
22242         }
22243     },
22244     
22245     onClick : function(e)
22246     {
22247         Roo.log('img onclick');
22248         this.fireEvent('click', this, e);
22249     }
22250    
22251 });
22252
22253  
22254 /*
22255  * - LGPL
22256  *
22257  * numberBox
22258  * 
22259  */
22260 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22261
22262 /**
22263  * @class Roo.bootstrap.dash.NumberBox
22264  * @extends Roo.bootstrap.Component
22265  * Bootstrap NumberBox class
22266  * @cfg {String} headline Box headline
22267  * @cfg {String} content Box content
22268  * @cfg {String} icon Box icon
22269  * @cfg {String} footer Footer text
22270  * @cfg {String} fhref Footer href
22271  * 
22272  * @constructor
22273  * Create a new NumberBox
22274  * @param {Object} config The config object
22275  */
22276
22277
22278 Roo.bootstrap.dash.NumberBox = function(config){
22279     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22280     
22281 };
22282
22283 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22284     
22285     headline : '',
22286     content : '',
22287     icon : '',
22288     footer : '',
22289     fhref : '',
22290     ficon : '',
22291     
22292     getAutoCreate : function(){
22293         
22294         var cfg = {
22295             tag : 'div',
22296             cls : 'small-box ',
22297             cn : [
22298                 {
22299                     tag : 'div',
22300                     cls : 'inner',
22301                     cn :[
22302                         {
22303                             tag : 'h3',
22304                             cls : 'roo-headline',
22305                             html : this.headline
22306                         },
22307                         {
22308                             tag : 'p',
22309                             cls : 'roo-content',
22310                             html : this.content
22311                         }
22312                     ]
22313                 }
22314             ]
22315         }
22316         
22317         if(this.icon){
22318             cfg.cn.push({
22319                 tag : 'div',
22320                 cls : 'icon',
22321                 cn :[
22322                     {
22323                         tag : 'i',
22324                         cls : 'ion ' + this.icon
22325                     }
22326                 ]
22327             });
22328         }
22329         
22330         if(this.footer){
22331             var footer = {
22332                 tag : 'a',
22333                 cls : 'small-box-footer',
22334                 href : this.fhref || '#',
22335                 html : this.footer
22336             };
22337             
22338             cfg.cn.push(footer);
22339             
22340         }
22341         
22342         return  cfg;
22343     },
22344
22345     onRender : function(ct,position){
22346         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22347
22348
22349        
22350                 
22351     },
22352
22353     setHeadline: function (value)
22354     {
22355         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22356     },
22357     
22358     setFooter: function (value, href)
22359     {
22360         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22361         
22362         if(href){
22363             this.el.select('a.small-box-footer',true).first().attr('href', href);
22364         }
22365         
22366     },
22367
22368     setContent: function (value)
22369     {
22370         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22371     },
22372
22373     initEvents: function() 
22374     {   
22375         
22376     }
22377     
22378 });
22379
22380  
22381 /*
22382  * - LGPL
22383  *
22384  * TabBox
22385  * 
22386  */
22387 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22388
22389 /**
22390  * @class Roo.bootstrap.dash.TabBox
22391  * @extends Roo.bootstrap.Component
22392  * Bootstrap TabBox class
22393  * @cfg {String} title Title of the TabBox
22394  * @cfg {String} icon Icon of the TabBox
22395  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22396  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22397  * 
22398  * @constructor
22399  * Create a new TabBox
22400  * @param {Object} config The config object
22401  */
22402
22403
22404 Roo.bootstrap.dash.TabBox = function(config){
22405     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22406     this.addEvents({
22407         // raw events
22408         /**
22409          * @event addpane
22410          * When a pane is added
22411          * @param {Roo.bootstrap.dash.TabPane} pane
22412          */
22413         "addpane" : true,
22414         /**
22415          * @event activatepane
22416          * When a pane is activated
22417          * @param {Roo.bootstrap.dash.TabPane} pane
22418          */
22419         "activatepane" : true
22420         
22421          
22422     });
22423     
22424     this.panes = [];
22425 };
22426
22427 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22428
22429     title : '',
22430     icon : false,
22431     showtabs : true,
22432     tabScrollable : false,
22433     
22434     getChildContainer : function()
22435     {
22436         return this.el.select('.tab-content', true).first();
22437     },
22438     
22439     getAutoCreate : function(){
22440         
22441         var header = {
22442             tag: 'li',
22443             cls: 'pull-left header',
22444             html: this.title,
22445             cn : []
22446         };
22447         
22448         if(this.icon){
22449             header.cn.push({
22450                 tag: 'i',
22451                 cls: 'fa ' + this.icon
22452             });
22453         }
22454         
22455         var h = {
22456             tag: 'ul',
22457             cls: 'nav nav-tabs pull-right',
22458             cn: [
22459                 header
22460             ]
22461         };
22462         
22463         if(this.tabScrollable){
22464             h = {
22465                 tag: 'div',
22466                 cls: 'tab-header',
22467                 cn: [
22468                     {
22469                         tag: 'ul',
22470                         cls: 'nav nav-tabs pull-right',
22471                         cn: [
22472                             header
22473                         ]
22474                     }
22475                 ]
22476             }
22477         }
22478         
22479         var cfg = {
22480             tag: 'div',
22481             cls: 'nav-tabs-custom',
22482             cn: [
22483                 h,
22484                 {
22485                     tag: 'div',
22486                     cls: 'tab-content no-padding',
22487                     cn: []
22488                 }
22489             ]
22490         }
22491
22492         return  cfg;
22493     },
22494     initEvents : function()
22495     {
22496         //Roo.log('add add pane handler');
22497         this.on('addpane', this.onAddPane, this);
22498     },
22499      /**
22500      * Updates the box title
22501      * @param {String} html to set the title to.
22502      */
22503     setTitle : function(value)
22504     {
22505         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22506     },
22507     onAddPane : function(pane)
22508     {
22509         this.panes.push(pane);
22510         //Roo.log('addpane');
22511         //Roo.log(pane);
22512         // tabs are rendere left to right..
22513         if(!this.showtabs){
22514             return;
22515         }
22516         
22517         var ctr = this.el.select('.nav-tabs', true).first();
22518          
22519          
22520         var existing = ctr.select('.nav-tab',true);
22521         var qty = existing.getCount();;
22522         
22523         
22524         var tab = ctr.createChild({
22525             tag : 'li',
22526             cls : 'nav-tab' + (qty ? '' : ' active'),
22527             cn : [
22528                 {
22529                     tag : 'a',
22530                     href:'#',
22531                     html : pane.title
22532                 }
22533             ]
22534         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22535         pane.tab = tab;
22536         
22537         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22538         if (!qty) {
22539             pane.el.addClass('active');
22540         }
22541         
22542                 
22543     },
22544     onTabClick : function(ev,un,ob,pane)
22545     {
22546         //Roo.log('tab - prev default');
22547         ev.preventDefault();
22548         
22549         
22550         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22551         pane.tab.addClass('active');
22552         //Roo.log(pane.title);
22553         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22554         // technically we should have a deactivate event.. but maybe add later.
22555         // and it should not de-activate the selected tab...
22556         this.fireEvent('activatepane', pane);
22557         pane.el.addClass('active');
22558         pane.fireEvent('activate');
22559         
22560         
22561     },
22562     
22563     getActivePane : function()
22564     {
22565         var r = false;
22566         Roo.each(this.panes, function(p) {
22567             if(p.el.hasClass('active')){
22568                 r = p;
22569                 return false;
22570             }
22571             
22572             return;
22573         });
22574         
22575         return r;
22576     }
22577     
22578     
22579 });
22580
22581  
22582 /*
22583  * - LGPL
22584  *
22585  * Tab pane
22586  * 
22587  */
22588 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22589 /**
22590  * @class Roo.bootstrap.TabPane
22591  * @extends Roo.bootstrap.Component
22592  * Bootstrap TabPane class
22593  * @cfg {Boolean} active (false | true) Default false
22594  * @cfg {String} title title of panel
22595
22596  * 
22597  * @constructor
22598  * Create a new TabPane
22599  * @param {Object} config The config object
22600  */
22601
22602 Roo.bootstrap.dash.TabPane = function(config){
22603     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22604     
22605     this.addEvents({
22606         // raw events
22607         /**
22608          * @event activate
22609          * When a pane is activated
22610          * @param {Roo.bootstrap.dash.TabPane} pane
22611          */
22612         "activate" : true
22613          
22614     });
22615 };
22616
22617 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22618     
22619     active : false,
22620     title : '',
22621     
22622     // the tabBox that this is attached to.
22623     tab : false,
22624      
22625     getAutoCreate : function() 
22626     {
22627         var cfg = {
22628             tag: 'div',
22629             cls: 'tab-pane'
22630         }
22631         
22632         if(this.active){
22633             cfg.cls += ' active';
22634         }
22635         
22636         return cfg;
22637     },
22638     initEvents  : function()
22639     {
22640         //Roo.log('trigger add pane handler');
22641         this.parent().fireEvent('addpane', this)
22642     },
22643     
22644      /**
22645      * Updates the tab title 
22646      * @param {String} html to set the title to.
22647      */
22648     setTitle: function(str)
22649     {
22650         if (!this.tab) {
22651             return;
22652         }
22653         this.title = str;
22654         this.tab.select('a', true).first().dom.innerHTML = str;
22655         
22656     }
22657     
22658     
22659     
22660 });
22661
22662  
22663
22664
22665  /*
22666  * - LGPL
22667  *
22668  * menu
22669  * 
22670  */
22671 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22672
22673 /**
22674  * @class Roo.bootstrap.menu.Menu
22675  * @extends Roo.bootstrap.Component
22676  * Bootstrap Menu class - container for Menu
22677  * @cfg {String} html Text of the menu
22678  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22679  * @cfg {String} icon Font awesome icon
22680  * @cfg {String} pos Menu align to (top | bottom) default bottom
22681  * 
22682  * 
22683  * @constructor
22684  * Create a new Menu
22685  * @param {Object} config The config object
22686  */
22687
22688
22689 Roo.bootstrap.menu.Menu = function(config){
22690     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22691     
22692     this.addEvents({
22693         /**
22694          * @event beforeshow
22695          * Fires before this menu is displayed
22696          * @param {Roo.bootstrap.menu.Menu} this
22697          */
22698         beforeshow : true,
22699         /**
22700          * @event beforehide
22701          * Fires before this menu is hidden
22702          * @param {Roo.bootstrap.menu.Menu} this
22703          */
22704         beforehide : true,
22705         /**
22706          * @event show
22707          * Fires after this menu is displayed
22708          * @param {Roo.bootstrap.menu.Menu} this
22709          */
22710         show : true,
22711         /**
22712          * @event hide
22713          * Fires after this menu is hidden
22714          * @param {Roo.bootstrap.menu.Menu} this
22715          */
22716         hide : true,
22717         /**
22718          * @event click
22719          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22720          * @param {Roo.bootstrap.menu.Menu} this
22721          * @param {Roo.EventObject} e
22722          */
22723         click : true
22724     });
22725     
22726 };
22727
22728 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22729     
22730     submenu : false,
22731     html : '',
22732     weight : 'default',
22733     icon : false,
22734     pos : 'bottom',
22735     
22736     
22737     getChildContainer : function() {
22738         if(this.isSubMenu){
22739             return this.el;
22740         }
22741         
22742         return this.el.select('ul.dropdown-menu', true).first();  
22743     },
22744     
22745     getAutoCreate : function()
22746     {
22747         var text = [
22748             {
22749                 tag : 'span',
22750                 cls : 'roo-menu-text',
22751                 html : this.html
22752             }
22753         ];
22754         
22755         if(this.icon){
22756             text.unshift({
22757                 tag : 'i',
22758                 cls : 'fa ' + this.icon
22759             })
22760         }
22761         
22762         
22763         var cfg = {
22764             tag : 'div',
22765             cls : 'btn-group',
22766             cn : [
22767                 {
22768                     tag : 'button',
22769                     cls : 'dropdown-button btn btn-' + this.weight,
22770                     cn : text
22771                 },
22772                 {
22773                     tag : 'button',
22774                     cls : 'dropdown-toggle btn btn-' + this.weight,
22775                     cn : [
22776                         {
22777                             tag : 'span',
22778                             cls : 'caret'
22779                         }
22780                     ]
22781                 },
22782                 {
22783                     tag : 'ul',
22784                     cls : 'dropdown-menu'
22785                 }
22786             ]
22787             
22788         };
22789         
22790         if(this.pos == 'top'){
22791             cfg.cls += ' dropup';
22792         }
22793         
22794         if(this.isSubMenu){
22795             cfg = {
22796                 tag : 'ul',
22797                 cls : 'dropdown-menu'
22798             }
22799         }
22800         
22801         return cfg;
22802     },
22803     
22804     onRender : function(ct, position)
22805     {
22806         this.isSubMenu = ct.hasClass('dropdown-submenu');
22807         
22808         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22809     },
22810     
22811     initEvents : function() 
22812     {
22813         if(this.isSubMenu){
22814             return;
22815         }
22816         
22817         this.hidden = true;
22818         
22819         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22820         this.triggerEl.on('click', this.onTriggerPress, this);
22821         
22822         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22823         this.buttonEl.on('click', this.onClick, this);
22824         
22825     },
22826     
22827     list : function()
22828     {
22829         if(this.isSubMenu){
22830             return this.el;
22831         }
22832         
22833         return this.el.select('ul.dropdown-menu', true).first();
22834     },
22835     
22836     onClick : function(e)
22837     {
22838         this.fireEvent("click", this, e);
22839     },
22840     
22841     onTriggerPress  : function(e)
22842     {   
22843         if (this.isVisible()) {
22844             this.hide();
22845         } else {
22846             this.show();
22847         }
22848     },
22849     
22850     isVisible : function(){
22851         return !this.hidden;
22852     },
22853     
22854     show : function()
22855     {
22856         this.fireEvent("beforeshow", this);
22857         
22858         this.hidden = false;
22859         this.el.addClass('open');
22860         
22861         Roo.get(document).on("mouseup", this.onMouseUp, this);
22862         
22863         this.fireEvent("show", this);
22864         
22865         
22866     },
22867     
22868     hide : function()
22869     {
22870         this.fireEvent("beforehide", this);
22871         
22872         this.hidden = true;
22873         this.el.removeClass('open');
22874         
22875         Roo.get(document).un("mouseup", this.onMouseUp);
22876         
22877         this.fireEvent("hide", this);
22878     },
22879     
22880     onMouseUp : function()
22881     {
22882         this.hide();
22883     }
22884     
22885 });
22886
22887  
22888  /*
22889  * - LGPL
22890  *
22891  * menu item
22892  * 
22893  */
22894 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22895
22896 /**
22897  * @class Roo.bootstrap.menu.Item
22898  * @extends Roo.bootstrap.Component
22899  * Bootstrap MenuItem class
22900  * @cfg {Boolean} submenu (true | false) default false
22901  * @cfg {String} html text of the item
22902  * @cfg {String} href the link
22903  * @cfg {Boolean} disable (true | false) default false
22904  * @cfg {Boolean} preventDefault (true | false) default true
22905  * @cfg {String} icon Font awesome icon
22906  * @cfg {String} pos Submenu align to (left | right) default right 
22907  * 
22908  * 
22909  * @constructor
22910  * Create a new Item
22911  * @param {Object} config The config object
22912  */
22913
22914
22915 Roo.bootstrap.menu.Item = function(config){
22916     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
22917     this.addEvents({
22918         /**
22919          * @event mouseover
22920          * Fires when the mouse is hovering over this menu
22921          * @param {Roo.bootstrap.menu.Item} this
22922          * @param {Roo.EventObject} e
22923          */
22924         mouseover : true,
22925         /**
22926          * @event mouseout
22927          * Fires when the mouse exits this menu
22928          * @param {Roo.bootstrap.menu.Item} this
22929          * @param {Roo.EventObject} e
22930          */
22931         mouseout : true,
22932         // raw events
22933         /**
22934          * @event click
22935          * The raw click event for the entire grid.
22936          * @param {Roo.EventObject} e
22937          */
22938         click : true
22939     });
22940 };
22941
22942 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
22943     
22944     submenu : false,
22945     href : '',
22946     html : '',
22947     preventDefault: true,
22948     disable : false,
22949     icon : false,
22950     pos : 'right',
22951     
22952     getAutoCreate : function()
22953     {
22954         var text = [
22955             {
22956                 tag : 'span',
22957                 cls : 'roo-menu-item-text',
22958                 html : this.html
22959             }
22960         ];
22961         
22962         if(this.icon){
22963             text.unshift({
22964                 tag : 'i',
22965                 cls : 'fa ' + this.icon
22966             })
22967         }
22968         
22969         var cfg = {
22970             tag : 'li',
22971             cn : [
22972                 {
22973                     tag : 'a',
22974                     href : this.href || '#',
22975                     cn : text
22976                 }
22977             ]
22978         };
22979         
22980         if(this.disable){
22981             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
22982         }
22983         
22984         if(this.submenu){
22985             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
22986             
22987             if(this.pos == 'left'){
22988                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
22989             }
22990         }
22991         
22992         return cfg;
22993     },
22994     
22995     initEvents : function() 
22996     {
22997         this.el.on('mouseover', this.onMouseOver, this);
22998         this.el.on('mouseout', this.onMouseOut, this);
22999         
23000         this.el.select('a', true).first().on('click', this.onClick, this);
23001         
23002     },
23003     
23004     onClick : function(e)
23005     {
23006         if(this.preventDefault){
23007             e.preventDefault();
23008         }
23009         
23010         this.fireEvent("click", this, e);
23011     },
23012     
23013     onMouseOver : function(e)
23014     {
23015         if(this.submenu && this.pos == 'left'){
23016             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23017         }
23018         
23019         this.fireEvent("mouseover", this, e);
23020     },
23021     
23022     onMouseOut : function(e)
23023     {
23024         this.fireEvent("mouseout", this, e);
23025     }
23026 });
23027
23028  
23029
23030  /*
23031  * - LGPL
23032  *
23033  * menu separator
23034  * 
23035  */
23036 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23037
23038 /**
23039  * @class Roo.bootstrap.menu.Separator
23040  * @extends Roo.bootstrap.Component
23041  * Bootstrap Separator class
23042  * 
23043  * @constructor
23044  * Create a new Separator
23045  * @param {Object} config The config object
23046  */
23047
23048
23049 Roo.bootstrap.menu.Separator = function(config){
23050     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23051 };
23052
23053 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23054     
23055     getAutoCreate : function(){
23056         var cfg = {
23057             tag : 'li',
23058             cls: 'divider'
23059         };
23060         
23061         return cfg;
23062     }
23063    
23064 });
23065
23066  
23067
23068  /*
23069  * - LGPL
23070  *
23071  * Tooltip
23072  * 
23073  */
23074
23075 /**
23076  * @class Roo.bootstrap.Tooltip
23077  * Bootstrap Tooltip class
23078  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23079  * to determine which dom element triggers the tooltip.
23080  * 
23081  * It needs to add support for additional attributes like tooltip-position
23082  * 
23083  * @constructor
23084  * Create a new Toolti
23085  * @param {Object} config The config object
23086  */
23087
23088 Roo.bootstrap.Tooltip = function(config){
23089     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23090 };
23091
23092 Roo.apply(Roo.bootstrap.Tooltip, {
23093     /**
23094      * @function init initialize tooltip monitoring.
23095      * @static
23096      */
23097     currentEl : false,
23098     currentTip : false,
23099     currentRegion : false,
23100     
23101     //  init : delay?
23102     
23103     init : function()
23104     {
23105         Roo.get(document).on('mouseover', this.enter ,this);
23106         Roo.get(document).on('mouseout', this.leave, this);
23107          
23108         
23109         this.currentTip = new Roo.bootstrap.Tooltip();
23110     },
23111     
23112     enter : function(ev)
23113     {
23114         var dom = ev.getTarget();
23115         
23116         //Roo.log(['enter',dom]);
23117         var el = Roo.fly(dom);
23118         if (this.currentEl) {
23119             //Roo.log(dom);
23120             //Roo.log(this.currentEl);
23121             //Roo.log(this.currentEl.contains(dom));
23122             if (this.currentEl == el) {
23123                 return;
23124             }
23125             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23126                 return;
23127             }
23128
23129         }
23130         
23131         
23132         
23133         if (this.currentTip.el) {
23134             this.currentTip.el.hide(); // force hiding...
23135         }    
23136         //Roo.log(ev);
23137         var bindEl = el;
23138         
23139         // you can not look for children, as if el is the body.. then everythign is the child..
23140         if (!el.attr('tooltip')) { //
23141             if (!el.select("[tooltip]").elements.length) {
23142                 return;
23143             }
23144             // is the mouse over this child...?
23145             bindEl = el.select("[tooltip]").first();
23146             var xy = ev.getXY();
23147             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23148                 //Roo.log("not in region.");
23149                 return;
23150             }
23151             //Roo.log("child element over..");
23152             
23153         }
23154         this.currentEl = bindEl;
23155         this.currentTip.bind(bindEl);
23156         this.currentRegion = Roo.lib.Region.getRegion(dom);
23157         this.currentTip.enter();
23158         
23159     },
23160     leave : function(ev)
23161     {
23162         var dom = ev.getTarget();
23163         //Roo.log(['leave',dom]);
23164         if (!this.currentEl) {
23165             return;
23166         }
23167         
23168         
23169         if (dom != this.currentEl.dom) {
23170             return;
23171         }
23172         var xy = ev.getXY();
23173         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23174             return;
23175         }
23176         // only activate leave if mouse cursor is outside... bounding box..
23177         
23178         
23179         
23180         
23181         if (this.currentTip) {
23182             this.currentTip.leave();
23183         }
23184         //Roo.log('clear currentEl');
23185         this.currentEl = false;
23186         
23187         
23188     },
23189     alignment : {
23190         'left' : ['r-l', [-2,0], 'right'],
23191         'right' : ['l-r', [2,0], 'left'],
23192         'bottom' : ['t-b', [0,2], 'top'],
23193         'top' : [ 'b-t', [0,-2], 'bottom']
23194     }
23195     
23196 });
23197
23198
23199 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23200     
23201     
23202     bindEl : false,
23203     
23204     delay : null, // can be { show : 300 , hide: 500}
23205     
23206     timeout : null,
23207     
23208     hoverState : null, //???
23209     
23210     placement : 'bottom', 
23211     
23212     getAutoCreate : function(){
23213     
23214         var cfg = {
23215            cls : 'tooltip',
23216            role : 'tooltip',
23217            cn : [
23218                 {
23219                     cls : 'tooltip-arrow'
23220                 },
23221                 {
23222                     cls : 'tooltip-inner'
23223                 }
23224            ]
23225         };
23226         
23227         return cfg;
23228     },
23229     bind : function(el)
23230     {
23231         this.bindEl = el;
23232     },
23233       
23234     
23235     enter : function () {
23236        
23237         if (this.timeout != null) {
23238             clearTimeout(this.timeout);
23239         }
23240         
23241         this.hoverState = 'in';
23242          //Roo.log("enter - show");
23243         if (!this.delay || !this.delay.show) {
23244             this.show();
23245             return;
23246         }
23247         var _t = this;
23248         this.timeout = setTimeout(function () {
23249             if (_t.hoverState == 'in') {
23250                 _t.show();
23251             }
23252         }, this.delay.show);
23253     },
23254     leave : function()
23255     {
23256         clearTimeout(this.timeout);
23257     
23258         this.hoverState = 'out';
23259          if (!this.delay || !this.delay.hide) {
23260             this.hide();
23261             return;
23262         }
23263        
23264         var _t = this;
23265         this.timeout = setTimeout(function () {
23266             //Roo.log("leave - timeout");
23267             
23268             if (_t.hoverState == 'out') {
23269                 _t.hide();
23270                 Roo.bootstrap.Tooltip.currentEl = false;
23271             }
23272         }, delay);
23273     },
23274     
23275     show : function ()
23276     {
23277         if (!this.el) {
23278             this.render(document.body);
23279         }
23280         // set content.
23281         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23282         
23283         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23284         
23285         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23286         
23287         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23288         
23289         var placement = typeof this.placement == 'function' ?
23290             this.placement.call(this, this.el, on_el) :
23291             this.placement;
23292             
23293         var autoToken = /\s?auto?\s?/i;
23294         var autoPlace = autoToken.test(placement);
23295         if (autoPlace) {
23296             placement = placement.replace(autoToken, '') || 'top';
23297         }
23298         
23299         //this.el.detach()
23300         //this.el.setXY([0,0]);
23301         this.el.show();
23302         //this.el.dom.style.display='block';
23303         this.el.addClass(placement);
23304         
23305         //this.el.appendTo(on_el);
23306         
23307         var p = this.getPosition();
23308         var box = this.el.getBox();
23309         
23310         if (autoPlace) {
23311             // fixme..
23312         }
23313         var align = Roo.bootstrap.Tooltip.alignment[placement];
23314         this.el.alignTo(this.bindEl, align[0],align[1]);
23315         //var arrow = this.el.select('.arrow',true).first();
23316         //arrow.set(align[2], 
23317         
23318         this.el.addClass('in fade');
23319         this.hoverState = null;
23320         
23321         if (this.el.hasClass('fade')) {
23322             // fade it?
23323         }
23324         
23325     },
23326     hide : function()
23327     {
23328          
23329         if (!this.el) {
23330             return;
23331         }
23332         //this.el.setXY([0,0]);
23333         this.el.removeClass('in');
23334         //this.el.hide();
23335         
23336     }
23337     
23338 });
23339  
23340
23341  /*
23342  * - LGPL
23343  *
23344  * Location Picker
23345  * 
23346  */
23347
23348 /**
23349  * @class Roo.bootstrap.LocationPicker
23350  * @extends Roo.bootstrap.Component
23351  * Bootstrap LocationPicker class
23352  * @cfg {Number} latitude Position when init default 0
23353  * @cfg {Number} longitude Position when init default 0
23354  * @cfg {Number} zoom default 15
23355  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23356  * @cfg {Boolean} mapTypeControl default false
23357  * @cfg {Boolean} disableDoubleClickZoom default false
23358  * @cfg {Boolean} scrollwheel default true
23359  * @cfg {Boolean} streetViewControl default false
23360  * @cfg {Number} radius default 0
23361  * @cfg {String} locationName
23362  * @cfg {Boolean} draggable default true
23363  * @cfg {Boolean} enableAutocomplete default false
23364  * @cfg {Boolean} enableReverseGeocode default true
23365  * @cfg {String} markerTitle
23366  * 
23367  * @constructor
23368  * Create a new LocationPicker
23369  * @param {Object} config The config object
23370  */
23371
23372
23373 Roo.bootstrap.LocationPicker = function(config){
23374     
23375     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23376     
23377     this.addEvents({
23378         /**
23379          * @event initial
23380          * Fires when the picker initialized.
23381          * @param {Roo.bootstrap.LocationPicker} this
23382          * @param {Google Location} location
23383          */
23384         initial : true,
23385         /**
23386          * @event positionchanged
23387          * Fires when the picker position changed.
23388          * @param {Roo.bootstrap.LocationPicker} this
23389          * @param {Google Location} location
23390          */
23391         positionchanged : true,
23392         /**
23393          * @event resize
23394          * Fires when the map resize.
23395          * @param {Roo.bootstrap.LocationPicker} this
23396          */
23397         resize : true,
23398         /**
23399          * @event show
23400          * Fires when the map show.
23401          * @param {Roo.bootstrap.LocationPicker} this
23402          */
23403         show : true,
23404         /**
23405          * @event hide
23406          * Fires when the map hide.
23407          * @param {Roo.bootstrap.LocationPicker} this
23408          */
23409         hide : true,
23410         /**
23411          * @event mapClick
23412          * Fires when click the map.
23413          * @param {Roo.bootstrap.LocationPicker} this
23414          * @param {Map event} e
23415          */
23416         mapClick : true,
23417         /**
23418          * @event mapRightClick
23419          * Fires when right click the map.
23420          * @param {Roo.bootstrap.LocationPicker} this
23421          * @param {Map event} e
23422          */
23423         mapRightClick : true,
23424         /**
23425          * @event markerClick
23426          * Fires when click the marker.
23427          * @param {Roo.bootstrap.LocationPicker} this
23428          * @param {Map event} e
23429          */
23430         markerClick : true,
23431         /**
23432          * @event markerRightClick
23433          * Fires when right click the marker.
23434          * @param {Roo.bootstrap.LocationPicker} this
23435          * @param {Map event} e
23436          */
23437         markerRightClick : true,
23438         /**
23439          * @event OverlayViewDraw
23440          * Fires when OverlayView Draw
23441          * @param {Roo.bootstrap.LocationPicker} this
23442          */
23443         OverlayViewDraw : true,
23444         /**
23445          * @event OverlayViewOnAdd
23446          * Fires when OverlayView Draw
23447          * @param {Roo.bootstrap.LocationPicker} this
23448          */
23449         OverlayViewOnAdd : true,
23450         /**
23451          * @event OverlayViewOnRemove
23452          * Fires when OverlayView Draw
23453          * @param {Roo.bootstrap.LocationPicker} this
23454          */
23455         OverlayViewOnRemove : true,
23456         /**
23457          * @event OverlayViewShow
23458          * Fires when OverlayView Draw
23459          * @param {Roo.bootstrap.LocationPicker} this
23460          * @param {Pixel} cpx
23461          */
23462         OverlayViewShow : true,
23463         /**
23464          * @event OverlayViewHide
23465          * Fires when OverlayView Draw
23466          * @param {Roo.bootstrap.LocationPicker} this
23467          */
23468         OverlayViewHide : true
23469     });
23470         
23471 };
23472
23473 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23474     
23475     gMapContext: false,
23476     
23477     latitude: 0,
23478     longitude: 0,
23479     zoom: 15,
23480     mapTypeId: false,
23481     mapTypeControl: false,
23482     disableDoubleClickZoom: false,
23483     scrollwheel: true,
23484     streetViewControl: false,
23485     radius: 0,
23486     locationName: '',
23487     draggable: true,
23488     enableAutocomplete: false,
23489     enableReverseGeocode: true,
23490     markerTitle: '',
23491     
23492     getAutoCreate: function()
23493     {
23494
23495         var cfg = {
23496             tag: 'div',
23497             cls: 'roo-location-picker'
23498         };
23499         
23500         return cfg
23501     },
23502     
23503     initEvents: function(ct, position)
23504     {       
23505         if(!this.el.getWidth() || this.isApplied()){
23506             return;
23507         }
23508         
23509         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23510         
23511         this.initial();
23512     },
23513     
23514     initial: function()
23515     {
23516         if(!this.mapTypeId){
23517             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23518         }
23519         
23520         this.gMapContext = this.GMapContext();
23521         
23522         this.initOverlayView();
23523         
23524         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23525         
23526         var _this = this;
23527                 
23528         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23529             _this.setPosition(_this.gMapContext.marker.position);
23530         });
23531         
23532         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23533             _this.fireEvent('mapClick', this, event);
23534             
23535         });
23536
23537         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23538             _this.fireEvent('mapRightClick', this, event);
23539             
23540         });
23541         
23542         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23543             _this.fireEvent('markerClick', this, event);
23544             
23545         });
23546
23547         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23548             _this.fireEvent('markerRightClick', this, event);
23549             
23550         });
23551         
23552         this.setPosition(this.gMapContext.location);
23553         
23554         this.fireEvent('initial', this, this.gMapContext.location);
23555     },
23556     
23557     initOverlayView: function()
23558     {
23559         var _this = this;
23560         
23561         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23562             
23563             draw: function()
23564             {
23565                 _this.fireEvent('OverlayViewDraw', _this);
23566             },
23567             
23568             onAdd: function()
23569             {
23570                 _this.fireEvent('OverlayViewOnAdd', _this);
23571             },
23572             
23573             onRemove: function()
23574             {
23575                 _this.fireEvent('OverlayViewOnRemove', _this);
23576             },
23577             
23578             show: function(cpx)
23579             {
23580                 _this.fireEvent('OverlayViewShow', _this, cpx);
23581             },
23582             
23583             hide: function()
23584             {
23585                 _this.fireEvent('OverlayViewHide', _this);
23586             }
23587             
23588         });
23589     },
23590     
23591     fromLatLngToContainerPixel: function(event)
23592     {
23593         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23594     },
23595     
23596     isApplied: function() 
23597     {
23598         return this.getGmapContext() == false ? false : true;
23599     },
23600     
23601     getGmapContext: function() 
23602     {
23603         return this.gMapContext
23604     },
23605     
23606     GMapContext: function() 
23607     {
23608         var position = new google.maps.LatLng(this.latitude, this.longitude);
23609         
23610         var _map = new google.maps.Map(this.el.dom, {
23611             center: position,
23612             zoom: this.zoom,
23613             mapTypeId: this.mapTypeId,
23614             mapTypeControl: this.mapTypeControl,
23615             disableDoubleClickZoom: this.disableDoubleClickZoom,
23616             scrollwheel: this.scrollwheel,
23617             streetViewControl: this.streetViewControl,
23618             locationName: this.locationName,
23619             draggable: this.draggable,
23620             enableAutocomplete: this.enableAutocomplete,
23621             enableReverseGeocode: this.enableReverseGeocode
23622         });
23623         
23624         var _marker = new google.maps.Marker({
23625             position: position,
23626             map: _map,
23627             title: this.markerTitle,
23628             draggable: this.draggable
23629         });
23630         
23631         return {
23632             map: _map,
23633             marker: _marker,
23634             circle: null,
23635             location: position,
23636             radius: this.radius,
23637             locationName: this.locationName,
23638             addressComponents: {
23639                 formatted_address: null,
23640                 addressLine1: null,
23641                 addressLine2: null,
23642                 streetName: null,
23643                 streetNumber: null,
23644                 city: null,
23645                 district: null,
23646                 state: null,
23647                 stateOrProvince: null
23648             },
23649             settings: this,
23650             domContainer: this.el.dom,
23651             geodecoder: new google.maps.Geocoder()
23652         };
23653     },
23654     
23655     drawCircle: function(center, radius, options) 
23656     {
23657         if (this.gMapContext.circle != null) {
23658             this.gMapContext.circle.setMap(null);
23659         }
23660         if (radius > 0) {
23661             radius *= 1;
23662             options = Roo.apply({}, options, {
23663                 strokeColor: "#0000FF",
23664                 strokeOpacity: .35,
23665                 strokeWeight: 2,
23666                 fillColor: "#0000FF",
23667                 fillOpacity: .2
23668             });
23669             
23670             options.map = this.gMapContext.map;
23671             options.radius = radius;
23672             options.center = center;
23673             this.gMapContext.circle = new google.maps.Circle(options);
23674             return this.gMapContext.circle;
23675         }
23676         
23677         return null;
23678     },
23679     
23680     setPosition: function(location) 
23681     {
23682         this.gMapContext.location = location;
23683         this.gMapContext.marker.setPosition(location);
23684         this.gMapContext.map.panTo(location);
23685         this.drawCircle(location, this.gMapContext.radius, {});
23686         
23687         var _this = this;
23688         
23689         if (this.gMapContext.settings.enableReverseGeocode) {
23690             this.gMapContext.geodecoder.geocode({
23691                 latLng: this.gMapContext.location
23692             }, function(results, status) {
23693                 
23694                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23695                     _this.gMapContext.locationName = results[0].formatted_address;
23696                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23697                     
23698                     _this.fireEvent('positionchanged', this, location);
23699                 }
23700             });
23701             
23702             return;
23703         }
23704         
23705         this.fireEvent('positionchanged', this, location);
23706     },
23707     
23708     resize: function()
23709     {
23710         google.maps.event.trigger(this.gMapContext.map, "resize");
23711         
23712         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23713         
23714         this.fireEvent('resize', this);
23715     },
23716     
23717     setPositionByLatLng: function(latitude, longitude)
23718     {
23719         this.setPosition(new google.maps.LatLng(latitude, longitude));
23720     },
23721     
23722     getCurrentPosition: function() 
23723     {
23724         return {
23725             latitude: this.gMapContext.location.lat(),
23726             longitude: this.gMapContext.location.lng()
23727         };
23728     },
23729     
23730     getAddressName: function() 
23731     {
23732         return this.gMapContext.locationName;
23733     },
23734     
23735     getAddressComponents: function() 
23736     {
23737         return this.gMapContext.addressComponents;
23738     },
23739     
23740     address_component_from_google_geocode: function(address_components) 
23741     {
23742         var result = {};
23743         
23744         for (var i = 0; i < address_components.length; i++) {
23745             var component = address_components[i];
23746             if (component.types.indexOf("postal_code") >= 0) {
23747                 result.postalCode = component.short_name;
23748             } else if (component.types.indexOf("street_number") >= 0) {
23749                 result.streetNumber = component.short_name;
23750             } else if (component.types.indexOf("route") >= 0) {
23751                 result.streetName = component.short_name;
23752             } else if (component.types.indexOf("neighborhood") >= 0) {
23753                 result.city = component.short_name;
23754             } else if (component.types.indexOf("locality") >= 0) {
23755                 result.city = component.short_name;
23756             } else if (component.types.indexOf("sublocality") >= 0) {
23757                 result.district = component.short_name;
23758             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23759                 result.stateOrProvince = component.short_name;
23760             } else if (component.types.indexOf("country") >= 0) {
23761                 result.country = component.short_name;
23762             }
23763         }
23764         
23765         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23766         result.addressLine2 = "";
23767         return result;
23768     },
23769     
23770     setZoomLevel: function(zoom)
23771     {
23772         this.gMapContext.map.setZoom(zoom);
23773     },
23774     
23775     show: function()
23776     {
23777         if(!this.el){
23778             return;
23779         }
23780         
23781         this.el.show();
23782         
23783         this.resize();
23784         
23785         this.fireEvent('show', this);
23786     },
23787     
23788     hide: function()
23789     {
23790         if(!this.el){
23791             return;
23792         }
23793         
23794         this.el.hide();
23795         
23796         this.fireEvent('hide', this);
23797     }
23798     
23799 });
23800
23801 Roo.apply(Roo.bootstrap.LocationPicker, {
23802     
23803     OverlayView : function(map, options)
23804     {
23805         options = options || {};
23806         
23807         this.setMap(map);
23808     }
23809     
23810     
23811 });/*
23812  * - LGPL
23813  *
23814  * Alert
23815  * 
23816  */
23817
23818 /**
23819  * @class Roo.bootstrap.Alert
23820  * @extends Roo.bootstrap.Component
23821  * Bootstrap Alert class
23822  * @cfg {String} title The title of alert
23823  * @cfg {String} html The content of alert
23824  * @cfg {String} weight (  success | info | warning | danger )
23825  * @cfg {String} faicon font-awesomeicon
23826  * 
23827  * @constructor
23828  * Create a new alert
23829  * @param {Object} config The config object
23830  */
23831
23832
23833 Roo.bootstrap.Alert = function(config){
23834     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23835     
23836 };
23837
23838 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23839     
23840     title: '',
23841     html: '',
23842     weight: false,
23843     faicon: false,
23844     
23845     getAutoCreate : function()
23846     {
23847         
23848         var cfg = {
23849             tag : 'div',
23850             cls : 'alert',
23851             cn : [
23852                 {
23853                     tag : 'i',
23854                     cls : 'roo-alert-icon'
23855                     
23856                 },
23857                 {
23858                     tag : 'b',
23859                     cls : 'roo-alert-title',
23860                     html : this.title
23861                 },
23862                 {
23863                     tag : 'span',
23864                     cls : 'roo-alert-text',
23865                     html : this.html
23866                 }
23867             ]
23868         };
23869         
23870         if(this.faicon){
23871             cfg.cn[0].cls += ' fa ' + this.faicon;
23872         }
23873         
23874         if(this.weight){
23875             cfg.cls += ' alert-' + this.weight;
23876         }
23877         
23878         return cfg;
23879     },
23880     
23881     initEvents: function() 
23882     {
23883         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23884     },
23885     
23886     setTitle : function(str)
23887     {
23888         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
23889     },
23890     
23891     setText : function(str)
23892     {
23893         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
23894     },
23895     
23896     setWeight : function(weight)
23897     {
23898         if(this.weight){
23899             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
23900         }
23901         
23902         this.weight = weight;
23903         
23904         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
23905     },
23906     
23907     setIcon : function(icon)
23908     {
23909         if(this.faicon){
23910             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
23911         }
23912         
23913         this.faicon = icon
23914         
23915         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
23916     },
23917     
23918     hide: function() 
23919     {
23920         this.el.hide();   
23921     },
23922     
23923     show: function() 
23924     {  
23925         this.el.show();   
23926     }
23927     
23928 });
23929
23930  
23931 /*
23932 * Licence: LGPL
23933 */
23934
23935 /**
23936  * @class Roo.bootstrap.UploadCropbox
23937  * @extends Roo.bootstrap.Component
23938  * Bootstrap UploadCropbox class
23939  * @cfg {String} emptyText show when image has been loaded
23940  * @cfg {Number} minWidth default 300
23941  * @cfg {Number} minHeight default 300
23942  * 
23943  * @constructor
23944  * Create a new UploadCropbox
23945  * @param {Object} config The config object
23946  */
23947
23948 Roo.bootstrap.UploadCropbox = function(config){
23949     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
23950     
23951     this.addEvents({
23952         /**
23953          * @event beforeSelectFile
23954          * Fire before select file
23955          * @param {Roo.bootstrap.UploadCropbox} this
23956          */
23957         "beforeselectfile" : true,
23958         /**
23959          * @event initial
23960          * Fire after initEvent
23961          * @param {Roo.bootstrap.UploadCropbox} this
23962          */
23963         "initial" : true,
23964         /**
23965          * @event crop
23966          * Fire after initEvent
23967          * @param {Roo.bootstrap.UploadCropbox} this
23968          * @param {String} imageData
23969          */
23970         "crop" : true,
23971         /**
23972          * @event prepare
23973          * Fire when preparing the file data
23974          * @param {Roo.bootstrap.UploadCropbox} this
23975          * @param {Object} file
23976          */
23977         "prepare" : true,
23978         /**
23979          * @event exception
23980          * Fire when get exception
23981          * @param {Roo.bootstrap.UploadCropbox} this
23982          * @param {Object} options
23983          */
23984         "exception" : true,
23985         /**
23986          * @event beforeloadimage
23987          * Fire before load the image
23988          * @param {Roo.bootstrap.UploadCropbox} this
23989          * @param {String} src
23990          */
23991         "beforeloadimage" : true
23992         
23993     });
23994 };
23995
23996 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
23997     
23998     emptyText : 'Click to upload image',
23999     scale : 0,
24000     baseScale : 1,
24001     rotate : 0,
24002     dragable : false,
24003     pinching : false,
24004     mouseX : 0,
24005     mouseY : 0,
24006     cropImageData : false,
24007     minWidth : 300,
24008     minHeight : 300,
24009     file : false,
24010     exif : {},
24011     baseRotate : 1,
24012     
24013     getAutoCreate : function()
24014     {
24015         var cfg = {
24016             tag : 'div',
24017             cls : 'roo-upload-cropbox',
24018             cn : [
24019                 {
24020                     tag : 'div',
24021                     cls : 'roo-upload-cropbox-image-section',
24022                     cn : [
24023                         {
24024                             tag : 'div',
24025                             cls : 'roo-upload-cropbox-canvas',
24026                             cn : [
24027                                 {
24028                                     tag : 'img',
24029                                     cls : 'roo-upload-cropbox-image'
24030                                 }
24031                             ]
24032                         },
24033                         {
24034                             tag : 'div',
24035                             cls : 'roo-upload-cropbox-thumb'
24036                         },
24037                         {
24038                             tag : 'div',
24039                             cls : 'roo-upload-cropbox-empty-notify',
24040                             html : this.emptyText
24041                         }
24042                     ]
24043                 },
24044                 {
24045                     tag : 'div',
24046                     cls : 'roo-upload-cropbox-footer-section',
24047                     cn : {
24048                         tag : 'div',
24049                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24050                         cn : [
24051                             {
24052                                 tag : 'div',
24053                                 cls : 'btn-group',
24054                                 cn : [
24055                                     {
24056                                         tag : 'button',
24057                                         cls : 'btn btn-default roo-upload-cropbox-rotate-left',
24058                                         html : '<i class="fa fa-undo"></i>'
24059                                     }
24060                                 ]
24061                             },
24062                             {
24063                                 tag : 'div',
24064                                 cls : 'btn-group',
24065                                 cn : [
24066                                     {
24067                                         tag : 'button',
24068                                         cls : 'btn btn-default roo-upload-cropbox-picture',
24069                                         html : '<i class="fa fa-picture-o"></i>'
24070                                     }
24071                                 ]
24072                             },
24073                             {
24074                                 tag : 'div',
24075                                 cls : 'btn-group',
24076                                 cn : [
24077                                     {
24078                                         tag : 'button',
24079                                         cls : 'btn btn-default roo-upload-cropbox-rotate-right',
24080                                         html : '<i class="fa fa-repeat"></i>'
24081                                     }
24082                                 ]
24083                             }
24084                         ]
24085                     }
24086                 }
24087             ]
24088         };
24089         
24090         return cfg;
24091     },
24092     
24093     initEvents : function()
24094     {
24095         this.imageSection = this.el.select('.roo-upload-cropbox-image-section', true).first();
24096         this.imageSection.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24097         
24098         this.imageCanvas = this.el.select('.roo-upload-cropbox-canvas', true).first();
24099         this.imageCanvas.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24100         
24101         this.image = this.el.select('.roo-upload-cropbox-image', true).first();
24102         this.image.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24103         
24104         this.thumb = this.el.select('.roo-upload-cropbox-thumb', true).first();
24105         this.thumb.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24106         this.thumb.hide();
24107         
24108         this.emptyNotify = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24109         this.emptyNotify.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24110         
24111         this.footerSection = this.el.select('.roo-upload-cropbox-footer-section', true).first();
24112         this.footerSection.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24113         this.footerSection.hide();
24114         
24115         this.rotateLeft = this.el.select('.roo-upload-cropbox-rotate-left', true).first();
24116         this.rotateLeft.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24117         
24118         this.pictureBtn = this.el.select('.roo-upload-cropbox-picture', true).first();
24119         this.pictureBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24120         
24121         this.rotateRight = this.el.select('.roo-upload-cropbox-rotate-right', true).first();
24122         this.rotateRight.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24123         
24124         this.calcThumbBoxSize();
24125         
24126         this.bind();
24127         
24128         this.fireEvent('initial', this);
24129     },
24130
24131     bind : function()
24132     {
24133         this.image.on('load', this.onLoadCanvasImage, this);
24134         
24135         if(!this.imageSectionHasOnClickEvent){
24136             this.imageSection.on('click', this.beforeSelectFile, this);
24137             this.imageSectionHasOnClickEvent = true;
24138         }
24139         
24140         if(Roo.isTouch){
24141             this.imageSection.on('touchstart', this.onTouchStart, this);
24142             this.imageSection.on('touchmove', this.onTouchMove, this);
24143             this.imageSection.on('touchend', this.onTouchEnd, this);
24144         }
24145         
24146         if(!Roo.isTouch){
24147             this.imageSection.on('mousedown', this.onMouseDown, this);
24148             this.imageSection.on('mousemove', this.onMouseMove, this);
24149             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24150             this.imageSection.on(mousewheel, this.onMouseWheel, this);
24151             Roo.get(document).on('mouseup', this.onMouseUp, this);
24152         }
24153         
24154         this.pictureBtn.on('click', this.beforeSelectFile, this);
24155         
24156         this.rotateLeft.on('click', this.onRotateLeft, this);
24157         
24158         this.rotateRight.on('click', this.onRotateRight, this);
24159         
24160     },
24161     
24162     reset : function()
24163     {    
24164         this.scale = 0;
24165         this.baseScale = 1;
24166         this.rotate = 0;
24167         this.dragable = false;
24168         this.pinching = false;
24169         this.mouseX = 0;
24170         this.mouseY = 0;
24171         this.cropImageData = false;
24172         
24173         this.imageCanvas.dom.removeAttribute('style');
24174         this.image.dom.removeAttribute('style');
24175         this.image.attr('src', '');
24176         
24177         if(!this.imageSectionHasOnClickEvent){
24178             this.imageSection.on('click', this.beforeSelectFile, this);
24179             this.imageSectionHasOnClickEvent = true;
24180         }
24181         
24182     },
24183     
24184     beforeSelectFile : function(e)
24185     {
24186         e.preventDefault();
24187         
24188         this.fireEvent('beforeselectfile', this);
24189     },
24190     
24191     loadCanvasImage : function(src)
24192     {   
24193         if(this.fireEvent('beforeloadimage', this, src) != false){
24194             this.reset();
24195             this.image.attr('src', src);
24196         }
24197     },
24198     
24199     onLoadCanvasImage : function(src)
24200     {   
24201         this.emptyNotify.hide();
24202         this.thumb.show();
24203         this.footerSection.show();
24204         
24205         this.placeThumbBox();
24206         
24207         this.Orientation();
24208         
24209         if(this.imageSectionHasOnClickEvent){
24210             this.imageSection.un('click', this.beforeSelectFile, this);
24211             this.imageSectionHasOnClickEvent = false;
24212         }
24213         
24214         this.image.OriginWidth = this.image.getWidth();
24215         this.image.OriginHeight = this.image.getHeight();
24216         
24217         this.fitThumbBox();
24218         
24219         this.image.setWidth(Math.ceil(this.image.OriginWidth * this.getScaleLevel(false)));
24220         this.image.setHeight(Math.ceil(this.image.OriginHeight * this.getScaleLevel(false)));
24221         
24222         this.setCanvasPosition();
24223     },
24224     
24225     setCanvasPosition : function()
24226     {   
24227         var pw = Math.ceil((this.imageSection.getWidth() - this.image.getWidth()) / 2);
24228         var ph = Math.ceil((this.imageSection.getHeight() - this.image.getHeight()) / 2);
24229         
24230         this.imageCanvas.setLeft(pw);
24231         this.imageCanvas.setTop(ph);
24232     },
24233     
24234     onMouseDown : function(e)
24235     {   
24236         e.stopEvent();
24237         
24238         this.dragable = true;
24239         this.pinching = false;
24240         
24241         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24242         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24243         
24244     },
24245     
24246     onMouseMove : function(e)
24247     {   
24248         e.stopEvent();
24249         
24250         if (!this.dragable){
24251             return;
24252         }
24253         
24254         var minX = Math.ceil(this.thumb.getLeft(true));
24255         var minY = Math.ceil(this.thumb.getTop(true));
24256         
24257         var maxX = Math.ceil(minX + this.thumb.getWidth() - this.image.getWidth());
24258         var maxY = Math.ceil(minY + this.thumb.getHeight() - this.image.getHeight());
24259         
24260         if(this.rotate == 90 || this.rotate == 270){
24261             minX = Math.ceil(this.thumb.getLeft(true) - (this.image.getWidth() - this.image.getHeight()) / 2);
24262             minY = Math.ceil(this.thumb.getTop(true) + (this.image.getWidth() - this.image.getHeight()) / 2);
24263             
24264             maxX = Math.ceil(minX + this.thumb.getWidth() - this.image.getHeight());
24265             maxY = Math.ceil(minY + this.thumb.getHeight() - this.image.getWidth());
24266         }
24267         
24268         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24269         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24270         
24271         x = x - this.mouseX;
24272         y = y - this.mouseY;
24273         
24274         var bgX = Math.ceil(x + this.imageCanvas.getLeft(true));
24275         var bgY = Math.ceil(y + this.imageCanvas.getTop(true));
24276         
24277         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24278         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24279         
24280         this.imageCanvas.setLeft(bgX);
24281         this.imageCanvas.setTop(bgY);
24282         
24283         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24284         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24285     },
24286     
24287     onMouseUp : function(e)
24288     {   
24289         e.stopEvent();
24290         
24291         this.dragable = false;
24292     },
24293     
24294     onMouseWheel : function(e)
24295     {   
24296         e.stopEvent();
24297         
24298         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24299         
24300         var width = Math.ceil(this.image.OriginWidth * this.getScaleLevel(false));
24301         var height = Math.ceil(this.image.OriginHeight * this.getScaleLevel(false));
24302         
24303         if(
24304                 e.getWheelDelta() == -1 &&
24305                 (
24306                     (
24307                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumb.getWidth() || height < this.thumb.getHeight())
24308                     )
24309                     ||
24310                     (
24311                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumb.getWidth() || width < this.thumb.getHeight())
24312                     )
24313                 )
24314         ){
24315             this.scale = (e.getWheelDelta() == 1) ? (this.scale - 1) : (this.scale + 1);
24316             return;
24317         }
24318         
24319         this.image.setWidth(width);
24320         this.image.setHeight(height);
24321         
24322         this.setCanvasPosition();
24323         
24324     },
24325     
24326     onRotateLeft : function(e)
24327     {
24328         e.stopEvent();
24329         
24330         if(
24331                 (
24332                     (this.rotate == 0 || this.rotate == 180) 
24333                     &&
24334                     (this.image.getHeight() < this.thumb.getWidth() || this.image.getWidth() < this.thumb.getHeight())
24335                 )
24336                 ||
24337                 (
24338                     (this.rotate == 90 || this.rotate == 270) 
24339                     &&
24340                     (this.image.getWidth() < this.thumb.getWidth() || this.image.getHeight() < this.thumb.getHeight())
24341                 )
24342                 
24343         ){
24344             return;
24345         }
24346         
24347         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24348
24349         this.imageCanvas.setStyle({
24350             '-ms-transform' : 'rotate(' + this.rotate + 'deg)',
24351             '-webkit-transform' : 'rotate(' + this.rotate + 'deg)',
24352             'transform' : 'rotate(' + this.rotate + 'deg)'
24353         });
24354
24355         this.setCanvasPosition();
24356         
24357     },
24358     
24359     onRotateRight : function(e)
24360     {
24361         e.stopEvent();
24362         
24363         if(
24364                 (
24365                     (this.rotate == 0 || this.rotate == 180) 
24366                     &&
24367                     (this.image.getHeight() < this.thumb.getWidth() || this.image.getWidth() < this.thumb.getHeight())
24368                 )
24369                 ||
24370                 (
24371                     (this.rotate == 90 || this.rotate == 270) 
24372                     &&
24373                     (this.image.getWidth() < this.thumb.getWidth() || this.image.getHeight() < this.thumb.getHeight())
24374                 )
24375                 
24376         ){
24377             return false;
24378         }
24379         
24380         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24381
24382         this.imageCanvas.setStyle({
24383             '-ms-transform' : 'rotate(' + this.rotate + 'deg)',
24384             '-webkit-transform' : 'rotate(' + this.rotate + 'deg)',
24385             'transform' : 'rotate(' + this.rotate + 'deg)'
24386         });
24387
24388         this.setCanvasPosition();
24389         
24390         
24391     },
24392     
24393     crop : function()
24394     {
24395         var baseRotateLevel = this.getBaseRotateLevel();
24396         
24397 //        this['crop' + baseRotateLevel]();
24398         Roo.log(baseRotateLevel);
24399         
24400         var canvas = document.createElement("canvas");
24401         
24402         var context = canvas.getContext("2d");
24403         
24404         canvas.width = this.minWidth;
24405         canvas.height = this.minHeight;
24406         
24407         var centerX = this.minWidth / 2;
24408         var centerY = this.minHeight / 2;
24409         
24410         var cropWidth = this.thumb.getWidth() * this.getScaleLevel(true);
24411         var cropHeight = this.thumb.getHeight() * this.getScaleLevel(true);
24412         
24413         var thumbX = Math.ceil(this.thumb.getLeft(true));
24414         var thumbY = Math.ceil(this.thumb.getTop(true));
24415         
24416         var x = (thumbX - this.imageCanvas.getLeft(true)) * this.getScaleLevel(true);
24417         var y = (thumbY - this.imageCanvas.getTop(true)) * this.getScaleLevel(true);
24418         
24419         if(this.rotate == 90){
24420             
24421             x = thumbY + (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getTop(true);
24422             y = this.image.getHeight() - this.thumb.getWidth() - (thumbX - (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getLeft(true));
24423             
24424             x = x * this.getScaleLevel(true);
24425             y = y * this.getScaleLevel(true);
24426             
24427             if(this.image.OriginWidth - cropHeight < x){
24428                 x = this.image.OriginWidth - cropHeight;
24429             }
24430
24431             if(this.image.OriginHeight - cropWidth < y){
24432                 y = this.image.OriginHeight - cropWidth;
24433             }
24434             
24435             x = x < 0 ? 0 : x;
24436             y = y < 0 ? 0 : y;
24437             
24438             cropWidth = this.thumb.getHeight() * this.getScaleLevel(true);
24439             cropHeight = this.thumb.getWidth() * this.getScaleLevel(true);
24440             
24441             canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24442             canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24443
24444             centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24445             centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24446             
24447             context.translate(centerX, centerY);
24448             context.rotate(this.rotate * Math.PI / 180);
24449             
24450             context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24451         
24452             var canvas2 = document.createElement("canvas");
24453             var context2 = canvas2.getContext("2d");
24454             
24455             canvas2.width = this.minWidth;
24456             canvas2.height = this.minHeight;
24457             
24458             context2.drawImage(canvas, Math.abs(this.minWidth - this.minHeight), 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24459     
24460             this.cropImageData = canvas2.toDataURL(this.file.type);
24461             
24462             this.fireEvent('crop', this, this.cropImageData);
24463             
24464             return;
24465         }
24466         
24467         if(this.rotate == 270){
24468             
24469             x = thumbY + (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getTop(true);
24470             y = thumbX - (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getLeft(true);
24471             
24472             x = (this.image.getWidth() - this.thumb.getHeight() - x) * this.getScaleLevel(true);
24473             y = y * this.getScaleLevel(true);
24474             
24475             if(this.image.OriginWidth - cropHeight < x){
24476                 x = this.image.OriginWidth - cropHeight;
24477             }
24478
24479             if(this.image.OriginHeight - cropWidth < y){
24480                 y = this.image.OriginHeight - cropWidth;
24481             }
24482
24483             x = x < 0 ? 0 : x;
24484             y = y < 0 ? 0 : y;
24485             
24486             cropWidth = this.thumb.getHeight() * this.getScaleLevel(true);
24487             cropHeight = this.thumb.getWidth() * this.getScaleLevel(true);
24488             
24489             canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24490             canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24491
24492             centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24493             centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24494             
24495             context.translate(centerX, centerY);
24496             context.rotate(this.rotate * Math.PI / 180);
24497             
24498             context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24499         
24500             var canvas2 = document.createElement("canvas");
24501             var context2 = canvas2.getContext("2d");
24502             
24503             canvas2.width = this.minWidth;
24504             canvas2.height = this.minHeight;
24505             
24506             context2.drawImage(canvas, 0, 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24507     
24508             this.cropImageData = canvas2.toDataURL(this.file.type);
24509             
24510             this.fireEvent('crop', this, this.cropImageData);
24511             
24512             return;
24513         }
24514         
24515         if(this.rotate == 180){
24516             x = this.image.OriginWidth - this.thumb.getWidth() * this.getScaleLevel(true) - x;
24517             y = this.image.OriginHeight - this.thumb.getHeight() * this.getScaleLevel(true) - y;
24518         }
24519         
24520         if(this.image.OriginWidth - cropWidth < x){
24521             x = this.image.OriginWidth - cropWidth;
24522         }
24523         
24524         if(this.image.OriginHeight - cropHeight < y){
24525             y = this.image.OriginHeight - cropHeight;
24526         }
24527         
24528         x = x < 0 ? 0 : x;
24529         y = y < 0 ? 0 : y;
24530         
24531         context.translate(centerX, centerY);
24532
24533         context.rotate(this.rotate * Math.PI / 180);
24534         
24535         context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, canvas.width, canvas.height);
24536         
24537         this.cropImageData = canvas.toDataURL(this.file.type);
24538         
24539         this.fireEvent('crop', this, this.cropImageData);
24540     },
24541     
24542     crop0 : function()
24543     {
24544         var canvas = document.createElement("canvas");
24545         
24546         var context = canvas.getContext("2d");
24547         
24548         canvas.width = this.minWidth;
24549         canvas.height = this.minHeight;
24550         
24551         var centerX = this.minWidth / 2;
24552         var centerY = this.minHeight / 2;
24553         
24554         var cropWidth = this.thumb.getWidth() * this.getScaleLevel(true);
24555         var cropHeight = this.thumb.getHeight() * this.getScaleLevel(true);
24556         
24557         var thumbX = Math.ceil(this.thumb.getLeft(true));
24558         var thumbY = Math.ceil(this.thumb.getTop(true));
24559         
24560         var x = (thumbX - this.imageCanvas.getLeft(true)) * this.getScaleLevel(true);
24561         var y = (thumbY - this.imageCanvas.getTop(true)) * this.getScaleLevel(true);
24562         
24563         if(this.rotate == 90){
24564             
24565             x = thumbY + (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getTop(true);
24566             y = this.image.getHeight() - this.thumb.getWidth() - (thumbX - (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getLeft(true));
24567             
24568             x = x * this.getScaleLevel(true);
24569             y = y * this.getScaleLevel(true);
24570             
24571             if(this.image.OriginWidth - cropHeight < x){
24572                 x = this.image.OriginWidth - cropHeight;
24573             }
24574
24575             if(this.image.OriginHeight - cropWidth < y){
24576                 y = this.image.OriginHeight - cropWidth;
24577             }
24578             
24579             x = x < 0 ? 0 : x;
24580             y = y < 0 ? 0 : y;
24581             
24582             cropWidth = this.thumb.getHeight() * this.getScaleLevel(true);
24583             cropHeight = this.thumb.getWidth() * this.getScaleLevel(true);
24584             
24585             canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24586             canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24587
24588             centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24589             centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24590             
24591             context.translate(centerX, centerY);
24592             context.rotate(this.rotate * Math.PI / 180);
24593             
24594             context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24595         
24596             var canvas2 = document.createElement("canvas");
24597             var context2 = canvas2.getContext("2d");
24598             
24599             canvas2.width = this.minWidth;
24600             canvas2.height = this.minHeight;
24601             
24602             context2.drawImage(canvas, Math.abs(this.minWidth - this.minHeight), 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24603     
24604             this.cropImageData = canvas2.toDataURL(this.file.type);
24605             
24606             this.fireEvent('crop', this, this.cropImageData);
24607             
24608             return;
24609         }
24610         
24611         if(this.rotate == 270){
24612             
24613             x = thumbY + (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getTop(true);
24614             y = thumbX - (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getLeft(true);
24615             
24616             x = (this.image.getWidth() - this.thumb.getHeight() - x) * this.getScaleLevel(true);
24617             y = y * this.getScaleLevel(true);
24618             
24619             if(this.image.OriginWidth - cropHeight < x){
24620                 x = this.image.OriginWidth - cropHeight;
24621             }
24622
24623             if(this.image.OriginHeight - cropWidth < y){
24624                 y = this.image.OriginHeight - cropWidth;
24625             }
24626
24627             x = x < 0 ? 0 : x;
24628             y = y < 0 ? 0 : y;
24629             
24630             cropWidth = this.thumb.getHeight() * this.getScaleLevel(true);
24631             cropHeight = this.thumb.getWidth() * this.getScaleLevel(true);
24632             
24633             canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24634             canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24635
24636             centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24637             centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24638             
24639             context.translate(centerX, centerY);
24640             context.rotate(this.rotate * Math.PI / 180);
24641             
24642             context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24643         
24644             var canvas2 = document.createElement("canvas");
24645             var context2 = canvas2.getContext("2d");
24646             
24647             canvas2.width = this.minWidth;
24648             canvas2.height = this.minHeight;
24649             
24650             context2.drawImage(canvas, 0, 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24651     
24652             this.cropImageData = canvas2.toDataURL(this.file.type);
24653             
24654             this.fireEvent('crop', this, this.cropImageData);
24655             
24656             return;
24657         }
24658         
24659         if(this.rotate == 180){
24660             x = this.image.OriginWidth - this.thumb.getWidth() * this.getScaleLevel(true) - x;
24661             y = this.image.OriginHeight - this.thumb.getHeight() * this.getScaleLevel(true) - y;
24662         }
24663         
24664         if(this.image.OriginWidth - cropWidth < x){
24665             x = this.image.OriginWidth - cropWidth;
24666         }
24667         
24668         if(this.image.OriginHeight - cropHeight < y){
24669             y = this.image.OriginHeight - cropHeight;
24670         }
24671         
24672         x = x < 0 ? 0 : x;
24673         y = y < 0 ? 0 : y;
24674         
24675         context.translate(centerX, centerY);
24676
24677         context.rotate(this.rotate * Math.PI / 180);
24678         
24679         context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, canvas.width, canvas.height);
24680         
24681         this.cropImageData = canvas.toDataURL(this.file.type);
24682         
24683         this.fireEvent('crop', this, this.cropImageData);
24684     },
24685     
24686     crop90 : function()
24687     {
24688         var canvas = document.createElement("canvas");
24689         
24690         var context = canvas.getContext("2d");
24691         
24692         canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24693         canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24694         
24695         var centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24696         var centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24697         
24698         var cropWidth = this.thumb.getWidth() * this.getScaleLevel(true);
24699         var cropHeight = this.thumb.getHeight() * this.getScaleLevel(true);
24700         
24701         var thumbX = Math.ceil(this.thumb.getLeft(true) + this.thumb.getWidth());
24702         var thumbY = Math.ceil(this.thumb.getTop(true));
24703         
24704         var x = (thumbY - this.imageCanvas.getTop(true)) * this.getScaleLevel(true);
24705         var y = (thumbX - this.imageCanvas.getLeft(true)) * this.getScaleLevel(true);
24706         
24707         
24708 //        if(this.rotate == 90){
24709 //            
24710 //            x = thumbY + (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getTop(true);
24711 //            y = this.image.getHeight() - this.thumb.getWidth() - (thumbX - (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getLeft(true));
24712 //            
24713 //            x = x * this.getScaleLevel(true);
24714 //            y = y * this.getScaleLevel(true);
24715 //            
24716 //            if(this.image.OriginWidth - cropHeight < x){
24717 //                x = this.image.OriginWidth - cropHeight;
24718 //            }
24719 //
24720 //            if(this.image.OriginHeight - cropWidth < y){
24721 //                y = this.image.OriginHeight - cropWidth;
24722 //            }
24723 //            
24724 //            x = x < 0 ? 0 : x;
24725 //            y = y < 0 ? 0 : y;
24726 //            
24727 //            cropWidth = this.thumb.getHeight() * this.getScaleLevel(true);
24728 //            cropHeight = this.thumb.getWidth() * this.getScaleLevel(true);
24729 //            
24730 //            canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24731 //            canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24732 //
24733 //            centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24734 //            centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24735 //            
24736 //            context.translate(centerX, centerY);
24737 //            context.rotate(this.rotate * Math.PI / 180);
24738 //            
24739 //            context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24740 //        
24741 //            var canvas2 = document.createElement("canvas");
24742 //            var context2 = canvas2.getContext("2d");
24743 //            
24744 //            canvas2.width = this.minWidth;
24745 //            canvas2.height = this.minHeight;
24746 //            
24747 //            context2.drawImage(canvas, Math.abs(this.minWidth - this.minHeight), 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24748 //    
24749 //            this.cropImageData = canvas2.toDataURL(this.file.type);
24750 //            
24751 //            this.fireEvent('crop', this, this.cropImageData);
24752 //            
24753 //            return;
24754 //        }
24755 //        
24756 //        if(this.rotate == 270){
24757 //            
24758 //            x = thumbY + (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getTop(true);
24759 //            y = thumbX - (this.image.getWidth() - this.image.getHeight()) / 2 - this.imageCanvas.getLeft(true);
24760 //            
24761 //            x = (this.image.getWidth() - this.thumb.getHeight() - x) * this.getScaleLevel(true);
24762 //            y = y * this.getScaleLevel(true);
24763 //            
24764 //            if(this.image.OriginWidth - cropHeight < x){
24765 //                x = this.image.OriginWidth - cropHeight;
24766 //            }
24767 //
24768 //            if(this.image.OriginHeight - cropWidth < y){
24769 //                y = this.image.OriginHeight - cropWidth;
24770 //            }
24771 //
24772 //            x = x < 0 ? 0 : x;
24773 //            y = y < 0 ? 0 : y;
24774 //            
24775 //            cropWidth = this.thumb.getHeight() * this.getScaleLevel(true);
24776 //            cropHeight = this.thumb.getWidth() * this.getScaleLevel(true);
24777 //            
24778 //            canvas.width = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24779 //            canvas.height = this.minWidth > this.minHeight ? this.minWidth : this.minHeight;
24780 //
24781 //            centerX = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24782 //            centerY = this.minWidth > this.minHeight ? (this.minWidth / 2) : (this.minHeight / 2);
24783 //            
24784 //            context.translate(centerX, centerY);
24785 //            context.rotate(this.rotate * Math.PI / 180);
24786 //            
24787 //            context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24788 //        
24789 //            var canvas2 = document.createElement("canvas");
24790 //            var context2 = canvas2.getContext("2d");
24791 //            
24792 //            canvas2.width = this.minWidth;
24793 //            canvas2.height = this.minHeight;
24794 //            
24795 //            context2.drawImage(canvas, 0, 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24796 //    
24797 //            this.cropImageData = canvas2.toDataURL(this.file.type);
24798 //            
24799 //            this.fireEvent('crop', this, this.cropImageData);
24800 //            
24801 //            return;
24802 //        }
24803 //        
24804 //        if(this.rotate == 180){
24805 //            x = this.image.OriginWidth - this.thumb.getWidth() * this.getScaleLevel(true) - x;
24806 //            y = this.image.OriginHeight - this.thumb.getHeight() * this.getScaleLevel(true) - y;
24807 //        }
24808         
24809         if(this.image.OriginWidth - cropWidth < y){
24810             y = this.image.OriginWidth - cropWidth;
24811         }
24812         
24813         if(this.image.OriginHeight - cropHeight < x){
24814             x = this.image.OriginHeight - cropHeight;
24815         }
24816         
24817         x = x < 0 ? 0 : x;
24818         y = y < 0 ? 0 : y;
24819         
24820         context.translate(centerX, centerY);
24821
24822         context.rotate(this.rotate * Math.PI / 180);
24823         
24824         alert(x);
24825         alert(y);
24826         alert(cropWidth);
24827         alert(cropHeight);
24828         context.drawImage(this.image.dom, x, y, cropWidth, cropHeight, centerX * -1, centerY * -1, this.minHeight, this.minWidth);
24829         
24830         window.open(canvas.toDataURL(this.file.type));
24831         return;
24832         
24833         var canvas2 = document.createElement("canvas");
24834         var context2 = canvas2.getContext("2d");
24835
24836         canvas2.width = this.minWidth;
24837         canvas2.height = this.minHeight;
24838
24839         context2.drawImage(canvas, 0, 0, this.minWidth, this.minHeight, 0, 0, this.minWidth, this.minHeight);
24840
24841         this.cropImageData = canvas2.toDataURL(this.file.type);
24842
24843         this.fireEvent('crop', this, this.cropImageData);
24844         
24845         return;
24846     },
24847     
24848     calcThumbBoxSize : function()
24849     {
24850         var width, height;
24851         
24852         height = 300;
24853         width = Math.ceil(this.minWidth * height / this.minHeight);
24854         
24855         if(this.minWidth > this.minHeight){
24856             width = 300;
24857             height = Math.ceil(this.minHeight * width / this.minWidth);
24858         }
24859         
24860         this.thumb.setStyle({
24861             width : width + 'px',
24862             height : height + 'px'
24863         });
24864
24865         return;
24866             
24867     },
24868     
24869     placeThumbBox : function()
24870     {
24871         var x = Math.ceil((this.imageSection.getWidth() - this.thumb.getWidth()) / 2 );
24872         var y = Math.ceil((this.imageSection.getHeight() - this.thumb.getHeight()) / 2);
24873         
24874         this.thumb.setLeft(x);
24875         this.thumb.setTop(y);
24876         
24877     },
24878     
24879     fitThumbBox : function()
24880     {
24881         var width = this.thumb.getWidth();
24882         var height = this.image.OriginHeight * width / this.image.OriginWidth;
24883         
24884         this.baseScale = width / this.image.OriginWidth;
24885         
24886         if(this.image.OriginWidth > this.image.OriginHeight){
24887             height = this.thumb.getHeight();
24888             width = this.image.OriginWidth * height / this.image.OriginHeight;
24889             
24890             this.baseScale = height / this.image.OriginHeight;
24891         }
24892         
24893         return;
24894     },
24895     
24896     Orientation : function()
24897     {
24898         this.baseRotate = 1;
24899         
24900         if(
24901                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) == 'undefined' || 
24902                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) == -1
24903         ){
24904             return;
24905         }
24906         
24907         this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
24908         
24909     },
24910     
24911     getBaseRotateLevel : function()
24912     {
24913         return (Roo.isIOS) ? Roo.bootstrap.UploadCropbox['Orientation']['iOS'][this.baseRotate] : Roo.bootstrap.UploadCropbox['Orientation']['Android'][this.baseRotate];
24914         
24915     },
24916     
24917     getScaleLevel : function(reverse)
24918     {
24919         if(reverse){
24920             return Math.pow(1.1, this.scale * -1) / this.baseScale;
24921         }
24922         
24923         return this.baseScale * Math.pow(1.1, this.scale);
24924     },
24925     
24926     onTouchStart : function(e)
24927     {
24928         e.stopEvent();
24929         
24930         var touches = e.browserEvent.touches;
24931         
24932         if(!touches){
24933             return;
24934         }
24935         
24936         if(touches.length == 1){
24937             this.onMouseDown(e);
24938             return;
24939         }
24940         
24941         if(touches.length != 2){
24942             return;
24943         }
24944         
24945         var coords = [];
24946         
24947         for(var i = 0, finger; finger = touches[i]; i++){
24948             coords.push(finger.pageX, finger.pageY);
24949         }
24950         
24951         var x = Math.pow(coords[0] - coords[2], 2);
24952         var y = Math.pow(coords[1] - coords[3], 2);
24953         
24954         this.startDistance =  Math.sqrt(x + y);
24955         
24956         this.startScale = this.scale;
24957         
24958         this.pinching = true;
24959         this.dragable = false;
24960         
24961     },
24962     
24963     onTouchMove : function(e)
24964     {
24965         e.stopEvent();
24966         
24967         if(!this.pinching && !this.dragable){
24968             return;
24969         }
24970         
24971         var touches = e.browserEvent.touches;
24972         
24973         if(!touches){
24974             return;
24975         }
24976         
24977         if(this.dragable){
24978             this.onMouseMove(e);
24979             return;
24980         }
24981         
24982         var coords = [];
24983         
24984         for(var i = 0, finger; finger = touches[i]; i++){
24985             coords.push(finger.pageX, finger.pageY);
24986         }
24987         
24988         var x = Math.pow(coords[0] - coords[2], 2);
24989         var y = Math.pow(coords[1] - coords[3], 2);
24990         
24991         this.endDistance =  Math.sqrt(x + y);
24992         
24993         var scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
24994         
24995         var width = Math.ceil(this.image.OriginWidth * this.baseScale * Math.pow(1.1, scale));
24996         var height = Math.ceil(this.image.OriginHeight * this.baseScale * Math.pow(1.1, scale));
24997         
24998         if(
24999                 this.endDistance / this.startDistance < 1 &&
25000                 (
25001                     (
25002                         (this.rotate == 0 || this.rotate == 180) && (width < this.thumb.getWidth() || height < this.thumb.getHeight())
25003                     )
25004                     ||
25005                     (
25006                         (this.rotate == 90 || this.rotate == 270) && (height < this.thumb.getWidth() || width < this.thumb.getHeight())
25007                     )
25008                 )
25009         ){
25010             return;
25011         }
25012         
25013         this.scale = scale;
25014         
25015         this.image.setWidth(width);
25016         this.image.setHeight(height);
25017         
25018         this.setCanvasPosition();
25019         
25020         
25021     },
25022     
25023     onTouchEnd : function(e)
25024     {
25025         e.stopEvent();
25026         
25027         this.pinching = false;
25028         this.dragable = false;
25029         
25030     },
25031     
25032     prepare : function(input)
25033     {
25034         if(!input.files || !input.files[0]){
25035             return;
25036         }
25037         
25038         this.file = input.files[0];
25039         
25040         var noMetaData = !(window.DataView  && this.file && this.file.size >= 12 && this.file.type === 'image/jpeg');
25041         
25042         if(noMetaData){ // ??? only for jpeg ???
25043             Roo.log('noMetaData');
25044             return;
25045         }
25046         
25047         var _this = this;
25048         
25049         if(this.fireEvent('prepare', this, this.file) != false){
25050             
25051             var reader = new FileReader();
25052             
25053             reader.onload = function (e) {
25054                 if (e.target.error) {
25055                     Roo.log(e.target.error);
25056                     return;
25057                 }
25058                 
25059                 var buffer = e.target.result,
25060                     dataView = new DataView(buffer),
25061                     offset = 2,
25062                     maxOffset = dataView.byteLength - 4,
25063                     markerBytes,
25064                     markerLength;
25065                 
25066                 if (dataView.getUint16(0) === 0xffd8) {
25067                     while (offset < maxOffset) {
25068                         markerBytes = dataView.getUint16(offset);
25069                         
25070                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25071                             markerLength = dataView.getUint16(offset + 2) + 2;
25072                             if (offset + markerLength > dataView.byteLength) {
25073                                 Roo.log('Invalid meta data: Invalid segment size.');
25074                                 break;
25075                             }
25076                             
25077                             if(markerBytes == 0xffe1){
25078                                 _this.parseExifData(
25079                                     dataView,
25080                                     offset,
25081                                     markerLength
25082                                 );
25083                             }
25084                             
25085                             offset += markerLength;
25086                             
25087                             continue;
25088                         }
25089                         
25090                         break;
25091                     }
25092                     
25093                 }
25094                 
25095                 var urlAPI = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL);
25096                 
25097                 if(!urlAPI){
25098                     return;
25099                 }
25100                 
25101                 var url = urlAPI.createObjectURL(_this.file);
25102                 
25103                 _this.loadCanvasImage(url);
25104                 
25105                 return;
25106             }
25107             
25108             reader.readAsArrayBuffer(this.file);
25109             
25110         }
25111         
25112     },
25113     
25114     parseExifData : function(dataView, offset, length)
25115     {
25116         var tiffOffset = offset + 10,
25117             littleEndian,
25118             dirOffset;
25119     
25120         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25121             // No Exif data, might be XMP data instead
25122             return;
25123         }
25124         
25125         // Check for the ASCII code for "Exif" (0x45786966):
25126         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25127             // No Exif data, might be XMP data instead
25128             return;
25129         }
25130         if (tiffOffset + 8 > dataView.byteLength) {
25131             Roo.log('Invalid Exif data: Invalid segment size.');
25132             return;
25133         }
25134         // Check for the two null bytes:
25135         if (dataView.getUint16(offset + 8) !== 0x0000) {
25136             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25137             return;
25138         }
25139         // Check the byte alignment:
25140         switch (dataView.getUint16(tiffOffset)) {
25141         case 0x4949:
25142             littleEndian = true;
25143             break;
25144         case 0x4D4D:
25145             littleEndian = false;
25146             break;
25147         default:
25148             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25149             return;
25150         }
25151         // Check for the TIFF tag marker (0x002A):
25152         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25153             Roo.log('Invalid Exif data: Missing TIFF marker.');
25154             return;
25155         }
25156         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25157         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25158         
25159         this.parseExifTags(
25160             dataView,
25161             tiffOffset,
25162             tiffOffset + dirOffset,
25163             littleEndian
25164         );
25165     },
25166     
25167     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25168     {
25169         var tagsNumber,
25170             dirEndOffset,
25171             i;
25172         if (dirOffset + 6 > dataView.byteLength) {
25173             Roo.log('Invalid Exif data: Invalid directory offset.');
25174             return;
25175         }
25176         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25177         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25178         if (dirEndOffset + 4 > dataView.byteLength) {
25179             Roo.log('Invalid Exif data: Invalid directory size.');
25180             return;
25181         }
25182         for (i = 0; i < tagsNumber; i += 1) {
25183             this.parseExifTag(
25184                 dataView,
25185                 tiffOffset,
25186                 dirOffset + 2 + 12 * i, // tag offset
25187                 littleEndian
25188             );
25189         }
25190         // Return the offset to the next directory:
25191         return dataView.getUint32(dirEndOffset, littleEndian);
25192     },
25193     
25194     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25195     {
25196         var tag = dataView.getUint16(offset, littleEndian);
25197         
25198         this.exif[tag] = this.getExifValue(
25199             dataView,
25200             tiffOffset,
25201             offset,
25202             dataView.getUint16(offset + 2, littleEndian), // tag type
25203             dataView.getUint32(offset + 4, littleEndian), // tag length
25204             littleEndian
25205         );
25206     },
25207     
25208     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25209     {
25210         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25211             tagSize,
25212             dataOffset,
25213             values,
25214             i,
25215             str,
25216             c;
25217     
25218         if (!tagType) {
25219             Roo.log('Invalid Exif data: Invalid tag type.');
25220             return;
25221         }
25222         
25223         tagSize = tagType.size * length;
25224         // Determine if the value is contained in the dataOffset bytes,
25225         // or if the value at the dataOffset is a pointer to the actual data:
25226         dataOffset = tagSize > 4 ?
25227                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25228         if (dataOffset + tagSize > dataView.byteLength) {
25229             Roo.log('Invalid Exif data: Invalid data offset.');
25230             return;
25231         }
25232         if (length === 1) {
25233             return tagType.getValue(dataView, dataOffset, littleEndian);
25234         }
25235         values = [];
25236         for (i = 0; i < length; i += 1) {
25237             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25238         }
25239         
25240         if (tagType.ascii) {
25241             str = '';
25242             // Concatenate the chars:
25243             for (i = 0; i < values.length; i += 1) {
25244                 c = values[i];
25245                 // Ignore the terminating NULL byte(s):
25246                 if (c === '\u0000') {
25247                     break;
25248                 }
25249                 str += c;
25250             }
25251             return str;
25252         }
25253         return values;
25254     }
25255     
25256 });
25257
25258 Roo.apply(Roo.bootstrap.UploadCropbox, {
25259     tags : {
25260         'Orientation': 0x0112
25261     },
25262     
25263     Orientation: {
25264         iOS : {
25265             1: 0, //'top-left',
25266 //            2: 'top-right',
25267             3: 180, //'bottom-right',
25268 //            4: 'bottom-left',
25269 //            5: 'left-top',
25270             6: 90, //'right-top',
25271 //            7: 'right-bottom',
25272             8: 270 //'left-bottom'
25273         },
25274         Android : {
25275             1: 0, //'top-left',
25276 //            2: 'top-right',
25277             3: 180, //'bottom-right',
25278 //            4: 'bottom-left',
25279 //            5: 'left-top',
25280             6: 270, //'right-top',
25281 //            7: 'right-bottom',
25282             8: 90 //'left-bottom'
25283         }
25284     },
25285     
25286     exifTagTypes : {
25287         // byte, 8-bit unsigned int:
25288         1: {
25289             getValue: function (dataView, dataOffset) {
25290                 return dataView.getUint8(dataOffset);
25291             },
25292             size: 1
25293         },
25294         // ascii, 8-bit byte:
25295         2: {
25296             getValue: function (dataView, dataOffset) {
25297                 return String.fromCharCode(dataView.getUint8(dataOffset));
25298             },
25299             size: 1,
25300             ascii: true
25301         },
25302         // short, 16 bit int:
25303         3: {
25304             getValue: function (dataView, dataOffset, littleEndian) {
25305                 return dataView.getUint16(dataOffset, littleEndian);
25306             },
25307             size: 2
25308         },
25309         // long, 32 bit int:
25310         4: {
25311             getValue: function (dataView, dataOffset, littleEndian) {
25312                 return dataView.getUint32(dataOffset, littleEndian);
25313             },
25314             size: 4
25315         },
25316         // rational = two long values, first is numerator, second is denominator:
25317         5: {
25318             getValue: function (dataView, dataOffset, littleEndian) {
25319                 return dataView.getUint32(dataOffset, littleEndian) /
25320                     dataView.getUint32(dataOffset + 4, littleEndian);
25321             },
25322             size: 8
25323         },
25324         // slong, 32 bit signed int:
25325         9: {
25326             getValue: function (dataView, dataOffset, littleEndian) {
25327                 return dataView.getInt32(dataOffset, littleEndian);
25328             },
25329             size: 4
25330         },
25331         // srational, two slongs, first is numerator, second is denominator:
25332         10: {
25333             getValue: function (dataView, dataOffset, littleEndian) {
25334                 return dataView.getInt32(dataOffset, littleEndian) /
25335                     dataView.getInt32(dataOffset + 4, littleEndian);
25336             },
25337             size: 8
25338         }
25339     }
25340 });